aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/bcma/sprom.c71
-rw-r--r--drivers/hid/hid-core.c3
-rw-r--r--drivers/hid/hid-debug.c15
-rw-r--r--drivers/hid/hid-steelseries.c9
-rw-r--r--drivers/idle/intel_idle.c21
-rw-r--r--drivers/md/dm-bufio.c24
-rw-r--r--drivers/md/dm-cache-metadata.c4
-rw-r--r--drivers/md/dm-cache-policy.h4
-rw-r--r--drivers/md/dm-cache-target.c100
-rw-r--r--drivers/md/dm-mpath.c1
-rw-r--r--drivers/md/dm-snap.c1
-rw-r--r--drivers/md/dm-stripe.c11
-rw-r--r--drivers/md/dm-table.c2
-rw-r--r--drivers/md/dm-thin-metadata.c36
-rw-r--r--drivers/md/dm-thin-metadata.h7
-rw-r--r--drivers/md/dm-thin.c200
-rw-r--r--drivers/md/persistent-data/dm-space-map-disk.c3
-rw-r--r--drivers/md/persistent-data/dm-space-map-metadata.c127
-rw-r--r--drivers/md/persistent-data/dm-space-map.h23
-rw-r--r--drivers/net/wireless/Kconfig1
-rw-r--r--drivers/net/wireless/Makefile2
-rw-r--r--drivers/net/wireless/ath/ath9k/Kconfig8
-rw-r--r--drivers/net/wireless/ath/ath9k/ani.c57
-rw-r--r--drivers/net/wireless/ath/ath9k/ani.h12
-rw-r--r--drivers/net/wireless/ath/ath9k/ar5008_phy.c6
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9002_hw.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9002_initvals.h14
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_mac.c5
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_paprd.c19
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_phy.c6
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h2
-rw-r--r--drivers/net/wireless/ath/ath9k/beacon.c8
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.c418
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.h59
-rw-r--r--drivers/net/wireless/ath/ath9k/htc.h7
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_beacon.c5
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_init.c7
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_main.c35
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_txrx.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c17
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.h13
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c19
-rw-r--r--drivers/net/wireless/ath/ath9k/link.c9
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.c16
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.h4
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c47
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c9
-rw-r--r--drivers/net/wireless/ath/ath9k/wow.c168
-rw-r--r--drivers/net/wireless/ath/wil6210/Kconfig12
-rw-r--r--drivers/net/wireless/ath/wil6210/Makefile21
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c4
-rw-r--r--drivers/net/wireless/ath/wil6210/debug.c69
-rw-r--r--drivers/net/wireless/ath/wil6210/debugfs.c8
-rw-r--r--drivers/net/wireless/ath/wil6210/interrupt.c29
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c24
-rw-r--r--drivers/net/wireless/ath/wil6210/netdev.c54
-rw-r--r--drivers/net/wireless/ath/wil6210/trace.c20
-rw-r--r--drivers/net/wireless/ath/wil6210/trace.h235
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.c175
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.h32
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h27
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c13
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c3
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/ampdu.c2
-rw-r--r--drivers/net/wireless/cw1200/Kconfig30
-rw-r--r--drivers/net/wireless/cw1200/Makefile21
-rw-r--r--drivers/net/wireless/cw1200/bh.c619
-rw-r--r--drivers/net/wireless/cw1200/bh.h28
-rw-r--r--drivers/net/wireless/cw1200/cw1200.h323
-rw-r--r--drivers/net/wireless/cw1200/cw1200_sdio.c425
-rw-r--r--drivers/net/wireless/cw1200/cw1200_spi.c463
-rw-r--r--drivers/net/wireless/cw1200/debug.c428
-rw-r--r--drivers/net/wireless/cw1200/debug.h93
-rw-r--r--drivers/net/wireless/cw1200/fwio.c520
-rw-r--r--drivers/net/wireless/cw1200/fwio.h39
-rw-r--r--drivers/net/wireless/cw1200/hwbus.h33
-rw-r--r--drivers/net/wireless/cw1200/hwio.c310
-rw-r--r--drivers/net/wireless/cw1200/hwio.h246
-rw-r--r--drivers/net/wireless/cw1200/main.c600
-rw-r--r--drivers/net/wireless/cw1200/pm.c367
-rw-r--r--drivers/net/wireless/cw1200/pm.h43
-rw-r--r--drivers/net/wireless/cw1200/queue.c583
-rw-r--r--drivers/net/wireless/cw1200/queue.h116
-rw-r--r--drivers/net/wireless/cw1200/scan.c461
-rw-r--r--drivers/net/wireless/cw1200/scan.h56
-rw-r--r--drivers/net/wireless/cw1200/sta.c2404
-rw-r--r--drivers/net/wireless/cw1200/sta.h123
-rw-r--r--drivers/net/wireless/cw1200/txrx.c1474
-rw-r--r--drivers/net/wireless/cw1200/txrx.h106
-rw-r--r--drivers/net/wireless/cw1200/wsm.c1823
-rw-r--r--drivers/net/wireless/cw1200/wsm.h1873
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2100.c2
-rw-r--r--drivers/net/wireless/iwlegacy/commands.h8
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/agn.h21
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/calib.c8
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/commands.h12
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/dev.h57
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/devices.c107
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/lib.c20
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/mac80211.c18
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/main.c45
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/power.c6
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rs.c8
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rx.c2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/scan.c12
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/tt.c2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/tx.c12
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/ucode.c10
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-1000.c3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-2000.c39
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-5000.c4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-6000.c32
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-7000.c53
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-config.h49
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-csr.h19
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-drv.c3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c15
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw.h7
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-modparams.h1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-nvm-parse.c20
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-nvm-parse.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-prph.h12
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h21
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/Makefile2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/bt-coex.c8
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/d3.c176
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/debugfs.c428
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h4
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h8
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-power.h93
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h1
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api.h233
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw.c37
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c36
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c262
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h200
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/nvm.c212
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c37
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c76
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/power.c196
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/quota.c25
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rs.c141
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rs.h17
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rx.c22
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/scan.c6
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.c13
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.h6
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tt.c512
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tx.c2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/utils.c41
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/drv.c50
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c51
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/tx.c17
-rw-r--r--drivers/net/wireless/mwifiex/Kconfig4
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.c77
-rw-r--r--drivers/net/wireless/mwifiex/cmdevt.c1
-rw-r--r--drivers/net/wireless/mwifiex/fw.h17
-rw-r--r--drivers/net/wireless/mwifiex/init.c53
-rw-r--r--drivers/net/wireless/mwifiex/join.c5
-rw-r--r--drivers/net/wireless/mwifiex/main.c14
-rw-r--r--drivers/net/wireless/mwifiex/main.h5
-rw-r--r--drivers/net/wireless/mwifiex/scan.c23
-rw-r--r--drivers/net/wireless/mwifiex/sdio.c463
-rw-r--r--drivers/net/wireless/mwifiex/sdio.h340
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmd.c62
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmdresp.c17
-rw-r--r--drivers/net/wireless/mwifiex/uap_cmd.c21
-rw-r--r--drivers/net/wireless/mwifiex/uap_event.c25
-rw-r--r--drivers/net/wireless/mwl8k.c11
-rw-r--r--drivers/net/wireless/p54/p54spi.c37
-rw-r--r--drivers/net/wireless/rt2x00/rt2800lib.c782
-rw-r--r--drivers/net/wireless/rt2x00/rt2800pci.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt2800usb.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c36
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00pci.c9
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.c44
-rw-r--r--drivers/net/wireless/rt2x00/rt61pci.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/dm.c2
-rw-r--r--drivers/pcmcia/m8xx_pcmcia.c140
-rw-r--r--drivers/platform/x86/Kconfig8
-rw-r--r--drivers/platform/x86/Makefile2
-rw-r--r--drivers/platform/x86/asus-nb-wmi.c9
-rw-r--r--drivers/platform/x86/dell-laptop.c10
-rw-r--r--drivers/platform/x86/dell-wmi-aio.c53
-rw-r--r--drivers/platform/x86/hp-wmi.c24
-rw-r--r--drivers/platform/x86/hp_accel.c3
-rw-r--r--drivers/platform/x86/ideapad-laptop.c6
-rw-r--r--drivers/platform/x86/pvpanic.c124
-rw-r--r--drivers/platform/x86/samsung-q10.c5
-rw-r--r--drivers/platform/x86/sony-laptop.c20
-rw-r--r--drivers/scsi/Kconfig2
-rw-r--r--drivers/scsi/aic94xx/aic94xx_dev.c24
-rw-r--r--drivers/scsi/aic94xx/aic94xx_hwi.c2
-rw-r--r--drivers/scsi/aic94xx/aic94xx_tmf.c2
-rw-r--r--drivers/scsi/be2iscsi/be.h2
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.c172
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.h27
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.c70
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.h2
-rw-r--r--drivers/scsi/be2iscsi/be_main.c375
-rw-r--r--drivers/scsi/be2iscsi/be_main.h29
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.c43
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.h35
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc.h8
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_els.c2
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_fcoe.c55
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_hwi.c8
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_io.c9
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_tgt.c2
-rw-r--r--drivers/scsi/csiostor/csio_lnode.h2
-rw-r--r--drivers/scsi/csiostor/csio_rnode.h2
-rw-r--r--drivers/scsi/fnic/fnic.h34
-rw-r--r--drivers/scsi/fnic/fnic_fcs.c564
-rw-r--r--drivers/scsi/fnic/fnic_fip.h68
-rw-r--r--drivers/scsi/fnic/fnic_main.c51
-rw-r--r--drivers/scsi/fnic/vnic_dev.c10
-rw-r--r--drivers/scsi/fnic/vnic_dev.h2
-rw-r--r--drivers/scsi/fnic/vnic_devcmd.h67
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c85
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.h7
-rw-r--r--drivers/scsi/ipr.c16
-rw-r--r--drivers/scsi/ipr.h2
-rw-r--r--drivers/scsi/isci/remote_device.c4
-rw-r--r--drivers/scsi/isci/remote_device.h2
-rw-r--r--drivers/scsi/isci/request.c6
-rw-r--r--drivers/scsi/isci/task.c2
-rw-r--r--drivers/scsi/libsas/sas_ata.c18
-rw-r--r--drivers/scsi/libsas/sas_discover.c34
-rw-r--r--drivers/scsi/libsas/sas_expander.c110
-rw-r--r--drivers/scsi/libsas/sas_internal.h10
-rw-r--r--drivers/scsi/libsas/sas_port.c2
-rw-r--r--drivers/scsi/lpfc/lpfc.h24
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c166
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.c39
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h1
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c3
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c113
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c91
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h1
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h7
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c607
-rw-r--r--drivers/scsi/lpfc/lpfc_logmsg.h1
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c9
-rw-r--r--drivers/scsi/lpfc/lpfc_mem.c14
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c25
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c798
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c106
-rw-r--r--drivers/scsi/lpfc/lpfc_sli4.h21
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.c25
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.h1
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_base.c3
-rw-r--r--drivers/scsi/mvsas/mv_init.c2
-rw-r--r--drivers/scsi/mvsas/mv_sas.c16
-rw-r--r--drivers/scsi/mvsas/mv_sas.h4
-rw-r--r--drivers/scsi/pm8001/Makefile7
-rw-r--r--drivers/scsi/pm8001/pm8001_ctl.c74
-rw-r--r--drivers/scsi/pm8001/pm8001_defs.h34
-rw-r--r--drivers/scsi/pm8001/pm8001_hwi.c817
-rw-r--r--drivers/scsi/pm8001/pm8001_hwi.h4
-rw-r--r--drivers/scsi/pm8001/pm8001_init.c383
-rw-r--r--drivers/scsi/pm8001/pm8001_sas.c119
-rw-r--r--drivers/scsi/pm8001/pm8001_sas.h181
-rw-r--r--drivers/scsi/pm8001/pm80xx_hwi.c4130
-rw-r--r--drivers/scsi/pm8001/pm80xx_hwi.h1523
-rw-r--r--drivers/scsi/qla2xxx/Kconfig4
-rw-r--r--drivers/scsi/qla2xxx/qla_mr.c6
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c2
-rw-r--r--drivers/scsi/qla4xxx/ql4_iocb.c1
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c45
-rw-r--r--drivers/scsi/qla4xxx/ql4_version.h2
-rw-r--r--drivers/scsi/scsi_debug.c97
-rw-r--r--drivers/scsi/scsi_error.c37
-rw-r--r--drivers/scsi/scsi_lib.c9
-rw-r--r--drivers/scsi/scsi_pm.c84
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c94
-rw-r--r--drivers/scsi/sd.c42
-rw-r--r--drivers/scsi/sd.h1
-rw-r--r--drivers/scsi/sd_dif.c8
-rw-r--r--drivers/scsi/ufs/Kconfig11
-rw-r--r--drivers/scsi/ufs/Makefile1
-rw-r--r--drivers/scsi/ufs/ufshcd-pltfrm.c217
-rw-r--r--drivers/scsi/ufs/ufshcd.c2
-rw-r--r--drivers/tty/serial/68328serial.c1
-rw-r--r--drivers/tty/serial/bcm63xx_uart.c1
-rw-r--r--drivers/tty/tty_audit.c104
-rw-r--r--drivers/video/au1100fb.c22
-rw-r--r--drivers/xen/Kconfig2
-rw-r--r--drivers/xen/events.c3
290 files changed, 30212 insertions, 3881 deletions
diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
index 8934298a638d..de15b4f4b237 100644
--- a/drivers/bcma/sprom.c
+++ b/drivers/bcma/sprom.c
@@ -72,12 +72,12 @@ fail:
* R/W ops.
**************************************************/
-static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom)
+static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom,
+ size_t words)
{
int i;
- for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++)
- sprom[i] = bcma_read16(bus->drv_cc.core,
- offset + (i * 2));
+ for (i = 0; i < words; i++)
+ sprom[i] = bcma_read16(bus->drv_cc.core, offset + (i * 2));
}
/**************************************************
@@ -124,29 +124,29 @@ static inline u8 bcma_crc8(u8 crc, u8 data)
return t[crc ^ data];
}
-static u8 bcma_sprom_crc(const u16 *sprom)
+static u8 bcma_sprom_crc(const u16 *sprom, size_t words)
{
int word;
u8 crc = 0xFF;
- for (word = 0; word < SSB_SPROMSIZE_WORDS_R4 - 1; word++) {
+ for (word = 0; word < words - 1; word++) {
crc = bcma_crc8(crc, sprom[word] & 0x00FF);
crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8);
}
- crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF);
+ crc = bcma_crc8(crc, sprom[words - 1] & 0x00FF);
crc ^= 0xFF;
return crc;
}
-static int bcma_sprom_check_crc(const u16 *sprom)
+static int bcma_sprom_check_crc(const u16 *sprom, size_t words)
{
u8 crc;
u8 expected_crc;
u16 tmp;
- crc = bcma_sprom_crc(sprom);
- tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC;
+ crc = bcma_sprom_crc(sprom, words);
+ tmp = sprom[words - 1] & SSB_SPROM_REVISION_CRC;
expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
if (crc != expected_crc)
return -EPROTO;
@@ -154,21 +154,25 @@ static int bcma_sprom_check_crc(const u16 *sprom)
return 0;
}
-static int bcma_sprom_valid(const u16 *sprom)
+static int bcma_sprom_valid(struct bcma_bus *bus, const u16 *sprom,
+ size_t words)
{
u16 revision;
int err;
- err = bcma_sprom_check_crc(sprom);
+ err = bcma_sprom_check_crc(sprom, words);
if (err)
return err;
- revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV;
- if (revision != 8 && revision != 9) {
+ revision = sprom[words - 1] & SSB_SPROM_REVISION_REV;
+ if (revision != 8 && revision != 9 && revision != 10) {
pr_err("Unsupported SPROM revision: %d\n", revision);
return -ENOENT;
}
+ bus->sprom.revision = revision;
+ bcma_debug(bus, "Found SPROM revision %d\n", revision);
+
return 0;
}
@@ -208,9 +212,6 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
ARRAY_SIZE(bus->sprom.core_pwr_info));
- bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] &
- SSB_SPROM_REVISION_REV;
-
for (i = 0; i < 3; i++) {
v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
*(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
@@ -502,7 +503,6 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
case BCMA_CHIP_ID_BCM4331:
present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT;
break;
-
case BCMA_CHIP_ID_BCM43224:
case BCMA_CHIP_ID_BCM43225:
/* for these chips OTP is always available */
@@ -550,7 +550,9 @@ int bcma_sprom_get(struct bcma_bus *bus)
{
u16 offset = BCMA_CC_SPROM;
u16 *sprom;
- int err = 0;
+ size_t sprom_sizes[] = { SSB_SPROMSIZE_WORDS_R4,
+ SSB_SPROMSIZE_WORDS_R10, };
+ int i, err = 0;
if (!bus->drv_cc.core)
return -EOPNOTSUPP;
@@ -579,32 +581,37 @@ int bcma_sprom_get(struct bcma_bus *bus)
}
}
- sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
- GFP_KERNEL);
- if (!sprom)
- return -ENOMEM;
-
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
bcma_debug(bus, "SPROM offset 0x%x\n", offset);
- bcma_sprom_read(bus, offset, sprom);
+ for (i = 0; i < ARRAY_SIZE(sprom_sizes); i++) {
+ size_t words = sprom_sizes[i];
+
+ sprom = kcalloc(words, sizeof(u16), GFP_KERNEL);
+ if (!sprom)
+ return -ENOMEM;
+
+ bcma_sprom_read(bus, offset, sprom, words);
+ err = bcma_sprom_valid(bus, sprom, words);
+ if (!err)
+ break;
+
+ kfree(sprom);
+ }
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
- err = bcma_sprom_valid(sprom);
if (err) {
- bcma_warn(bus, "invalid sprom read from the PCIe card, try to use fallback sprom\n");
+ bcma_warn(bus, "Invalid SPROM read from the PCIe card, trying to use fallback SPROM\n");
err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
- goto out;
+ } else {
+ bcma_sprom_extract_r8(bus, sprom);
+ kfree(sprom);
}
- bcma_sprom_extract_r8(bus, sprom);
-
-out:
- kfree(sprom);
return err;
}
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 6961bbeab3ed..264f55099940 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1685,6 +1685,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) },
@@ -2341,7 +2342,7 @@ struct hid_device *hid_allocate_device(void)
init_waitqueue_head(&hdev->debug_wait);
INIT_LIST_HEAD(&hdev->debug_list);
- mutex_init(&hdev->debug_list_lock);
+ spin_lock_init(&hdev->debug_list_lock);
sema_init(&hdev->driver_lock, 1);
sema_init(&hdev->driver_input_lock, 1);
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 7e56cb3855e3..8453214ec376 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -579,15 +579,16 @@ void hid_debug_event(struct hid_device *hdev, char *buf)
{
int i;
struct hid_debug_list *list;
+ unsigned long flags;
- mutex_lock(&hdev->debug_list_lock);
+ spin_lock_irqsave(&hdev->debug_list_lock, flags);
list_for_each_entry(list, &hdev->debug_list, node) {
for (i = 0; i < strlen(buf); i++)
list->hid_debug_buf[(list->tail + i) % HID_DEBUG_BUFSIZE] =
buf[i];
list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
}
- mutex_unlock(&hdev->debug_list_lock);
+ spin_unlock_irqrestore(&hdev->debug_list_lock, flags);
wake_up_interruptible(&hdev->debug_wait);
}
@@ -977,6 +978,7 @@ static int hid_debug_events_open(struct inode *inode, struct file *file)
{
int err = 0;
struct hid_debug_list *list;
+ unsigned long flags;
if (!(list = kzalloc(sizeof(struct hid_debug_list), GFP_KERNEL))) {
err = -ENOMEM;
@@ -992,9 +994,9 @@ static int hid_debug_events_open(struct inode *inode, struct file *file)
file->private_data = list;
mutex_init(&list->read_mutex);
- mutex_lock(&list->hdev->debug_list_lock);
+ spin_lock_irqsave(&list->hdev->debug_list_lock, flags);
list_add_tail(&list->node, &list->hdev->debug_list);
- mutex_unlock(&list->hdev->debug_list_lock);
+ spin_unlock_irqrestore(&list->hdev->debug_list_lock, flags);
out:
return err;
@@ -1088,10 +1090,11 @@ static unsigned int hid_debug_events_poll(struct file *file, poll_table *wait)
static int hid_debug_events_release(struct inode *inode, struct file *file)
{
struct hid_debug_list *list = file->private_data;
+ unsigned long flags;
- mutex_lock(&list->hdev->debug_list_lock);
+ spin_lock_irqsave(&list->hdev->debug_list_lock, flags);
list_del(&list->node);
- mutex_unlock(&list->hdev->debug_list_lock);
+ spin_unlock_irqrestore(&list->hdev->debug_list_lock, flags);
kfree(list->hid_debug_buf);
kfree(list);
diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c
index 9b0efb0083fe..d16491192112 100644
--- a/drivers/hid/hid-steelseries.c
+++ b/drivers/hid/hid-steelseries.c
@@ -18,7 +18,8 @@
#include "hid-ids.h"
-#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \
+ (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES))
#define SRWS1_NUMBER_LEDS 15
struct steelseries_srws1_data {
__u16 led_state;
@@ -107,7 +108,8 @@ static __u8 steelseries_srws1_rdesc_fixed[] = {
0xC0 /* End Collection */
};
-#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \
+ (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES))
static void steelseries_srws1_set_leds(struct hid_device *hdev, __u16 leds)
{
struct list_head *report_list = &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
@@ -370,7 +372,8 @@ MODULE_DEVICE_TABLE(hid, steelseries_srws1_devices);
static struct hid_driver steelseries_srws1_driver = {
.name = "steelseries_srws1",
.id_table = steelseries_srws1_devices,
-#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \
+ (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES))
.probe = steelseries_srws1_probe,
.remove = steelseries_srws1_remove,
#endif
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index 0e8fab1913df..fa6964d8681a 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -273,6 +273,27 @@ static struct cpuidle_state hsw_cstates[CPUIDLE_STATE_MAX] = {
.target_residency = 500,
.enter = &intel_idle },
{
+ .name = "C8-HSW",
+ .desc = "MWAIT 0x40",
+ .flags = MWAIT2flg(0x40) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 300,
+ .target_residency = 900,
+ .enter = &intel_idle },
+ {
+ .name = "C9-HSW",
+ .desc = "MWAIT 0x50",
+ .flags = MWAIT2flg(0x50) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 600,
+ .target_residency = 1800,
+ .enter = &intel_idle },
+ {
+ .name = "C10-HSW",
+ .desc = "MWAIT 0x60",
+ .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 2600,
+ .target_residency = 7700,
+ .enter = &intel_idle },
+ {
.enter = NULL }
};
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index c6083132c4b8..0387e05cdb98 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -319,6 +319,9 @@ static void __cache_size_refresh(void)
static void *alloc_buffer_data(struct dm_bufio_client *c, gfp_t gfp_mask,
enum data_mode *data_mode)
{
+ unsigned noio_flag;
+ void *ptr;
+
if (c->block_size <= DM_BUFIO_BLOCK_SIZE_SLAB_LIMIT) {
*data_mode = DATA_MODE_SLAB;
return kmem_cache_alloc(DM_BUFIO_CACHE(c), gfp_mask);
@@ -332,7 +335,26 @@ static void *alloc_buffer_data(struct dm_bufio_client *c, gfp_t gfp_mask,
}
*data_mode = DATA_MODE_VMALLOC;
- return __vmalloc(c->block_size, gfp_mask, PAGE_KERNEL);
+
+ /*
+ * __vmalloc allocates the data pages and auxiliary structures with
+ * gfp_flags that were specified, but pagetables are always allocated
+ * with GFP_KERNEL, no matter what was specified as gfp_mask.
+ *
+ * Consequently, we must set per-process flag PF_MEMALLOC_NOIO so that
+ * all allocations done by this process (including pagetables) are done
+ * as if GFP_NOIO was specified.
+ */
+
+ if (gfp_mask & __GFP_NORETRY)
+ noio_flag = memalloc_noio_save();
+
+ ptr = __vmalloc(c->block_size, gfp_mask, PAGE_KERNEL);
+
+ if (gfp_mask & __GFP_NORETRY)
+ memalloc_noio_restore(noio_flag);
+
+ return ptr;
}
/*
diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c
index 83e995fece88..1af7255bbffb 100644
--- a/drivers/md/dm-cache-metadata.c
+++ b/drivers/md/dm-cache-metadata.c
@@ -1044,7 +1044,7 @@ void dm_cache_metadata_get_stats(struct dm_cache_metadata *cmd,
struct dm_cache_statistics *stats)
{
down_read(&cmd->root_lock);
- memcpy(stats, &cmd->stats, sizeof(*stats));
+ *stats = cmd->stats;
up_read(&cmd->root_lock);
}
@@ -1052,7 +1052,7 @@ void dm_cache_metadata_set_stats(struct dm_cache_metadata *cmd,
struct dm_cache_statistics *stats)
{
down_write(&cmd->root_lock);
- memcpy(&cmd->stats, stats, sizeof(*stats));
+ cmd->stats = *stats;
up_write(&cmd->root_lock);
}
diff --git a/drivers/md/dm-cache-policy.h b/drivers/md/dm-cache-policy.h
index 558bdfdabf5f..33369ca9614f 100644
--- a/drivers/md/dm-cache-policy.h
+++ b/drivers/md/dm-cache-policy.h
@@ -130,8 +130,8 @@ struct dm_cache_policy {
*
* Must not block.
*
- * Returns 1 iff in cache, 0 iff not, < 0 on error (-EWOULDBLOCK
- * would be typical).
+ * Returns 0 if in cache, -ENOENT if not, < 0 for other errors
+ * (-EWOULDBLOCK would be typical).
*/
int (*lookup)(struct dm_cache_policy *p, dm_oblock_t oblock, dm_cblock_t *cblock);
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index 10744091e6ca..df44b60e66f2 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -205,7 +205,7 @@ struct per_bio_data {
/*
* writethrough fields. These MUST remain at the end of this
* structure and the 'cache' member must be the first as it
- * is used to determine the offsetof the writethrough fields.
+ * is used to determine the offset of the writethrough fields.
*/
struct cache *cache;
dm_cblock_t cblock;
@@ -393,7 +393,7 @@ static int get_cell(struct cache *cache,
return r;
}
- /*----------------------------------------------------------------*/
+/*----------------------------------------------------------------*/
static bool is_dirty(struct cache *cache, dm_cblock_t b)
{
@@ -419,6 +419,7 @@ static void clear_dirty(struct cache *cache, dm_oblock_t oblock, dm_cblock_t cbl
}
/*----------------------------------------------------------------*/
+
static bool block_size_is_power_of_two(struct cache *cache)
{
return cache->sectors_per_block_shift >= 0;
@@ -667,7 +668,7 @@ static void writethrough_endio(struct bio *bio, int err)
/*
* We can't issue this bio directly, since we're in interrupt
- * context. So it get's put on a bio list for processing by the
+ * context. So it gets put on a bio list for processing by the
* worker thread.
*/
defer_writethrough_bio(pb->cache, bio);
@@ -1445,6 +1446,7 @@ static void do_worker(struct work_struct *ws)
static void do_waker(struct work_struct *ws)
{
struct cache *cache = container_of(to_delayed_work(ws), struct cache, waker);
+ policy_tick(cache->policy);
wake_worker(cache);
queue_delayed_work(cache->wq, &cache->waker, COMMIT_PERIOD);
}
@@ -1809,7 +1811,37 @@ static int parse_cache_args(struct cache_args *ca, int argc, char **argv,
static struct kmem_cache *migration_cache;
-static int set_config_values(struct dm_cache_policy *p, int argc, const char **argv)
+#define NOT_CORE_OPTION 1
+
+static int process_config_option(struct cache *cache, const char *key, const char *value)
+{
+ unsigned long tmp;
+
+ if (!strcasecmp(key, "migration_threshold")) {
+ if (kstrtoul(value, 10, &tmp))
+ return -EINVAL;
+
+ cache->migration_threshold = tmp;
+ return 0;
+ }
+
+ return NOT_CORE_OPTION;
+}
+
+static int set_config_value(struct cache *cache, const char *key, const char *value)
+{
+ int r = process_config_option(cache, key, value);
+
+ if (r == NOT_CORE_OPTION)
+ r = policy_set_config_value(cache->policy, key, value);
+
+ if (r)
+ DMWARN("bad config value for %s: %s", key, value);
+
+ return r;
+}
+
+static int set_config_values(struct cache *cache, int argc, const char **argv)
{
int r = 0;
@@ -1819,12 +1851,9 @@ static int set_config_values(struct dm_cache_policy *p, int argc, const char **a
}
while (argc) {
- r = policy_set_config_value(p, argv[0], argv[1]);
- if (r) {
- DMWARN("policy_set_config_value failed: key = '%s', value = '%s'",
- argv[0], argv[1]);
- return r;
- }
+ r = set_config_value(cache, argv[0], argv[1]);
+ if (r)
+ break;
argc -= 2;
argv += 2;
@@ -1836,8 +1865,6 @@ static int set_config_values(struct dm_cache_policy *p, int argc, const char **a
static int create_cache_policy(struct cache *cache, struct cache_args *ca,
char **error)
{
- int r;
-
cache->policy = dm_cache_policy_create(ca->policy_name,
cache->cache_size,
cache->origin_sectors,
@@ -1847,14 +1874,7 @@ static int create_cache_policy(struct cache *cache, struct cache_args *ca,
return -ENOMEM;
}
- r = set_config_values(cache->policy, ca->policy_argc, ca->policy_argv);
- if (r) {
- *error = "Error setting cache policy's config values";
- dm_cache_policy_destroy(cache->policy);
- cache->policy = NULL;
- }
-
- return r;
+ return 0;
}
/*
@@ -1886,7 +1906,7 @@ static sector_t calculate_discard_block_size(sector_t cache_block_size,
return discard_block_size;
}
-#define DEFAULT_MIGRATION_THRESHOLD (2048 * 100)
+#define DEFAULT_MIGRATION_THRESHOLD 2048
static int cache_create(struct cache_args *ca, struct cache **result)
{
@@ -1911,7 +1931,7 @@ static int cache_create(struct cache_args *ca, struct cache **result)
ti->discards_supported = true;
ti->discard_zeroes_data_unsupported = true;
- memcpy(&cache->features, &ca->features, sizeof(cache->features));
+ cache->features = ca->features;
ti->per_bio_data_size = get_per_bio_data_size(cache);
cache->callbacks.congested_fn = cache_is_congested;
@@ -1948,7 +1968,15 @@ static int cache_create(struct cache_args *ca, struct cache **result)
r = create_cache_policy(cache, ca, error);
if (r)
goto bad;
+
cache->policy_nr_args = ca->policy_argc;
+ cache->migration_threshold = DEFAULT_MIGRATION_THRESHOLD;
+
+ r = set_config_values(cache, ca->policy_argc, ca->policy_argv);
+ if (r) {
+ *error = "Error setting cache policy's config values";
+ goto bad;
+ }
cmd = dm_cache_metadata_open(cache->metadata_dev->bdev,
ca->block_size, may_format,
@@ -1967,10 +1995,10 @@ static int cache_create(struct cache_args *ca, struct cache **result)
INIT_LIST_HEAD(&cache->quiesced_migrations);
INIT_LIST_HEAD(&cache->completed_migrations);
INIT_LIST_HEAD(&cache->need_commit_migrations);
- cache->migration_threshold = DEFAULT_MIGRATION_THRESHOLD;
atomic_set(&cache->nr_migrations, 0);
init_waitqueue_head(&cache->migration_wait);
+ r = -ENOMEM;
cache->nr_dirty = 0;
cache->dirty_bitset = alloc_bitset(from_cblock(cache->cache_size));
if (!cache->dirty_bitset) {
@@ -2517,23 +2545,6 @@ err:
DMEMIT("Error");
}
-#define NOT_CORE_OPTION 1
-
-static int process_config_option(struct cache *cache, char **argv)
-{
- unsigned long tmp;
-
- if (!strcasecmp(argv[0], "migration_threshold")) {
- if (kstrtoul(argv[1], 10, &tmp))
- return -EINVAL;
-
- cache->migration_threshold = tmp;
- return 0;
- }
-
- return NOT_CORE_OPTION;
-}
-
/*
* Supports <key> <value>.
*
@@ -2541,17 +2552,12 @@ static int process_config_option(struct cache *cache, char **argv)
*/
static int cache_message(struct dm_target *ti, unsigned argc, char **argv)
{
- int r;
struct cache *cache = ti->private;
if (argc != 2)
return -EINVAL;
- r = process_config_option(cache, argv);
- if (r == NOT_CORE_OPTION)
- return policy_set_config_value(cache->policy, argv[0], argv[1]);
-
- return r;
+ return set_config_value(cache, argv[0], argv[1]);
}
static int cache_iterate_devices(struct dm_target *ti,
@@ -2609,7 +2615,7 @@ static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits)
static struct target_type cache_target = {
.name = "cache",
- .version = {1, 1, 0},
+ .version = {1, 1, 1},
.module = THIS_MODULE,
.ctr = cache_ctr,
.dtr = cache_dtr,
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 51bb81676be3..bdf26f5bd326 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -907,6 +907,7 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc,
ti->num_flush_bios = 1;
ti->num_discard_bios = 1;
+ ti->num_write_same_bios = 1;
return 0;
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index c0e07026a8d1..c434e5aab2df 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -1121,6 +1121,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
s->pending_pool = mempool_create_slab_pool(MIN_IOS, pending_cache);
if (!s->pending_pool) {
ti->error = "Could not allocate mempool for pending exceptions";
+ r = -ENOMEM;
goto bad_pending_pool;
}
diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c
index ea5e878a30b9..d907ca6227ce 100644
--- a/drivers/md/dm-stripe.c
+++ b/drivers/md/dm-stripe.c
@@ -94,7 +94,7 @@ static int get_stripe(struct dm_target *ti, struct stripe_c *sc,
static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
struct stripe_c *sc;
- sector_t width;
+ sector_t width, tmp_len;
uint32_t stripes;
uint32_t chunk_size;
int r;
@@ -116,15 +116,16 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
}
width = ti->len;
- if (sector_div(width, chunk_size)) {
+ if (sector_div(width, stripes)) {
ti->error = "Target length not divisible by "
- "chunk size";
+ "number of stripes";
return -EINVAL;
}
- if (sector_div(width, stripes)) {
+ tmp_len = width;
+ if (sector_div(tmp_len, chunk_size)) {
ti->error = "Target length not divisible by "
- "number of stripes";
+ "chunk size";
return -EINVAL;
}
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index e50dad0c65f4..1ff252ab7d46 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -1442,7 +1442,7 @@ static bool dm_table_supports_write_same(struct dm_table *t)
return false;
if (!ti->type->iterate_devices ||
- !ti->type->iterate_devices(ti, device_not_write_same_capable, NULL))
+ ti->type->iterate_devices(ti, device_not_write_same_capable, NULL))
return false;
}
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c
index 00cee02f8fc9..60bce435f4fa 100644
--- a/drivers/md/dm-thin-metadata.c
+++ b/drivers/md/dm-thin-metadata.c
@@ -1645,12 +1645,12 @@ int dm_thin_get_highest_mapped_block(struct dm_thin_device *td,
return r;
}
-static int __resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
+static int __resize_space_map(struct dm_space_map *sm, dm_block_t new_count)
{
int r;
dm_block_t old_count;
- r = dm_sm_get_nr_blocks(pmd->data_sm, &old_count);
+ r = dm_sm_get_nr_blocks(sm, &old_count);
if (r)
return r;
@@ -1658,11 +1658,11 @@ static int __resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
return 0;
if (new_count < old_count) {
- DMERR("cannot reduce size of data device");
+ DMERR("cannot reduce size of space map");
return -EINVAL;
}
- return dm_sm_extend(pmd->data_sm, new_count - old_count);
+ return dm_sm_extend(sm, new_count - old_count);
}
int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
@@ -1671,7 +1671,19 @@ int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
down_write(&pmd->root_lock);
if (!pmd->fail_io)
- r = __resize_data_dev(pmd, new_count);
+ r = __resize_space_map(pmd->data_sm, new_count);
+ up_write(&pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_resize_metadata_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
+{
+ int r = -EINVAL;
+
+ down_write(&pmd->root_lock);
+ if (!pmd->fail_io)
+ r = __resize_space_map(pmd->metadata_sm, new_count);
up_write(&pmd->root_lock);
return r;
@@ -1684,3 +1696,17 @@ void dm_pool_metadata_read_only(struct dm_pool_metadata *pmd)
dm_bm_set_read_only(pmd->bm);
up_write(&pmd->root_lock);
}
+
+int dm_pool_register_metadata_threshold(struct dm_pool_metadata *pmd,
+ dm_block_t threshold,
+ dm_sm_threshold_fn fn,
+ void *context)
+{
+ int r;
+
+ down_write(&pmd->root_lock);
+ r = dm_sm_register_threshold_callback(pmd->metadata_sm, threshold, fn, context);
+ up_write(&pmd->root_lock);
+
+ return r;
+}
diff --git a/drivers/md/dm-thin-metadata.h b/drivers/md/dm-thin-metadata.h
index 0cecc3702885..845ebbe589a9 100644
--- a/drivers/md/dm-thin-metadata.h
+++ b/drivers/md/dm-thin-metadata.h
@@ -8,6 +8,7 @@
#define DM_THIN_METADATA_H
#include "persistent-data/dm-block-manager.h"
+#include "persistent-data/dm-space-map.h"
#define THIN_METADATA_BLOCK_SIZE 4096
@@ -185,6 +186,7 @@ int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result);
* blocks would be lost.
*/
int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_size);
+int dm_pool_resize_metadata_dev(struct dm_pool_metadata *pmd, dm_block_t new_size);
/*
* Flicks the underlying block manager into read only mode, so you know
@@ -192,6 +194,11 @@ int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_size);
*/
void dm_pool_metadata_read_only(struct dm_pool_metadata *pmd);
+int dm_pool_register_metadata_threshold(struct dm_pool_metadata *pmd,
+ dm_block_t threshold,
+ dm_sm_threshold_fn fn,
+ void *context);
+
/*----------------------------------------------------------------*/
#endif
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index 004ad1652b73..759cffc45cab 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -922,7 +922,7 @@ static int alloc_data_block(struct thin_c *tc, dm_block_t *result)
return r;
if (free_blocks <= pool->low_water_blocks && !pool->low_water_triggered) {
- DMWARN("%s: reached low water mark, sending event.",
+ DMWARN("%s: reached low water mark for data device: sending event.",
dm_device_name(pool->pool_md));
spin_lock_irqsave(&pool->lock, flags);
pool->low_water_triggered = 1;
@@ -1281,6 +1281,10 @@ static void process_bio_fail(struct thin_c *tc, struct bio *bio)
bio_io_error(bio);
}
+/*
+ * FIXME: should we also commit due to size of transaction, measured in
+ * metadata blocks?
+ */
static int need_commit_due_to_time(struct pool *pool)
{
return jiffies < pool->last_commit_jiffies ||
@@ -1909,6 +1913,56 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
return r;
}
+static void metadata_low_callback(void *context)
+{
+ struct pool *pool = context;
+
+ DMWARN("%s: reached low water mark for metadata device: sending event.",
+ dm_device_name(pool->pool_md));
+
+ dm_table_event(pool->ti->table);
+}
+
+static sector_t get_metadata_dev_size(struct block_device *bdev)
+{
+ sector_t metadata_dev_size = i_size_read(bdev->bd_inode) >> SECTOR_SHIFT;
+ char buffer[BDEVNAME_SIZE];
+
+ if (metadata_dev_size > THIN_METADATA_MAX_SECTORS_WARNING) {
+ DMWARN("Metadata device %s is larger than %u sectors: excess space will not be used.",
+ bdevname(bdev, buffer), THIN_METADATA_MAX_SECTORS);
+ metadata_dev_size = THIN_METADATA_MAX_SECTORS_WARNING;
+ }
+
+ return metadata_dev_size;
+}
+
+static dm_block_t get_metadata_dev_size_in_blocks(struct block_device *bdev)
+{
+ sector_t metadata_dev_size = get_metadata_dev_size(bdev);
+
+ sector_div(metadata_dev_size, THIN_METADATA_BLOCK_SIZE >> SECTOR_SHIFT);
+
+ return metadata_dev_size;
+}
+
+/*
+ * When a metadata threshold is crossed a dm event is triggered, and
+ * userland should respond by growing the metadata device. We could let
+ * userland set the threshold, like we do with the data threshold, but I'm
+ * not sure they know enough to do this well.
+ */
+static dm_block_t calc_metadata_threshold(struct pool_c *pt)
+{
+ /*
+ * 4M is ample for all ops with the possible exception of thin
+ * device deletion which is harmless if it fails (just retry the
+ * delete after you've grown the device).
+ */
+ dm_block_t quarter = get_metadata_dev_size_in_blocks(pt->metadata_dev->bdev) / 4;
+ return min((dm_block_t)1024ULL /* 4M */, quarter);
+}
+
/*
* thin-pool <metadata dev> <data dev>
* <data block size (sectors)>
@@ -1931,8 +1985,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
unsigned long block_size;
dm_block_t low_water_blocks;
struct dm_dev *metadata_dev;
- sector_t metadata_dev_size;
- char b[BDEVNAME_SIZE];
+ fmode_t metadata_mode;
/*
* FIXME Remove validation from scope of lock.
@@ -1944,19 +1997,32 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
r = -EINVAL;
goto out_unlock;
}
+
as.argc = argc;
as.argv = argv;
- r = dm_get_device(ti, argv[0], FMODE_READ | FMODE_WRITE, &metadata_dev);
+ /*
+ * Set default pool features.
+ */
+ pool_features_init(&pf);
+
+ dm_consume_args(&as, 4);
+ r = parse_pool_features(&as, &pf, ti);
+ if (r)
+ goto out_unlock;
+
+ metadata_mode = FMODE_READ | ((pf.mode == PM_READ_ONLY) ? 0 : FMODE_WRITE);
+ r = dm_get_device(ti, argv[0], metadata_mode, &metadata_dev);
if (r) {
ti->error = "Error opening metadata block device";
goto out_unlock;
}
- metadata_dev_size = i_size_read(metadata_dev->bdev->bd_inode) >> SECTOR_SHIFT;
- if (metadata_dev_size > THIN_METADATA_MAX_SECTORS_WARNING)
- DMWARN("Metadata device %s is larger than %u sectors: excess space will not be used.",
- bdevname(metadata_dev->bdev, b), THIN_METADATA_MAX_SECTORS);
+ /*
+ * Run for the side-effect of possibly issuing a warning if the
+ * device is too big.
+ */
+ (void) get_metadata_dev_size(metadata_dev->bdev);
r = dm_get_device(ti, argv[1], FMODE_READ | FMODE_WRITE, &data_dev);
if (r) {
@@ -1979,16 +2045,6 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
goto out;
}
- /*
- * Set default pool features.
- */
- pool_features_init(&pf);
-
- dm_consume_args(&as, 4);
- r = parse_pool_features(&as, &pf, ti);
- if (r)
- goto out;
-
pt = kzalloc(sizeof(*pt), GFP_KERNEL);
if (!pt) {
r = -ENOMEM;
@@ -2040,6 +2096,13 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
}
ti->private = pt;
+ r = dm_pool_register_metadata_threshold(pt->pool->pmd,
+ calc_metadata_threshold(pt),
+ metadata_low_callback,
+ pool);
+ if (r)
+ goto out_free_pt;
+
pt->callbacks.congested_fn = pool_is_congested;
dm_table_add_target_callbacks(ti->table, &pt->callbacks);
@@ -2079,18 +2142,7 @@ static int pool_map(struct dm_target *ti, struct bio *bio)
return r;
}
-/*
- * Retrieves the number of blocks of the data device from
- * the superblock and compares it to the actual device size,
- * thus resizing the data device in case it has grown.
- *
- * This both copes with opening preallocated data devices in the ctr
- * being followed by a resume
- * -and-
- * calling the resume method individually after userspace has
- * grown the data device in reaction to a table event.
- */
-static int pool_preresume(struct dm_target *ti)
+static int maybe_resize_data_dev(struct dm_target *ti, bool *need_commit)
{
int r;
struct pool_c *pt = ti->private;
@@ -2098,12 +2150,7 @@ static int pool_preresume(struct dm_target *ti)
sector_t data_size = ti->len;
dm_block_t sb_data_size;
- /*
- * Take control of the pool object.
- */
- r = bind_control_target(pool, ti);
- if (r)
- return r;
+ *need_commit = false;
(void) sector_div(data_size, pool->sectors_per_block);
@@ -2114,7 +2161,7 @@ static int pool_preresume(struct dm_target *ti)
}
if (data_size < sb_data_size) {
- DMERR("pool target too small, is %llu blocks (expected %llu)",
+ DMERR("pool target (%llu blocks) too small: expected %llu",
(unsigned long long)data_size, sb_data_size);
return -EINVAL;
@@ -2122,17 +2169,90 @@ static int pool_preresume(struct dm_target *ti)
r = dm_pool_resize_data_dev(pool->pmd, data_size);
if (r) {
DMERR("failed to resize data device");
- /* FIXME Stricter than necessary: Rollback transaction instead here */
set_pool_mode(pool, PM_READ_ONLY);
return r;
}
- (void) commit_or_fallback(pool);
+ *need_commit = true;
}
return 0;
}
+static int maybe_resize_metadata_dev(struct dm_target *ti, bool *need_commit)
+{
+ int r;
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+ dm_block_t metadata_dev_size, sb_metadata_dev_size;
+
+ *need_commit = false;
+
+ metadata_dev_size = get_metadata_dev_size(pool->md_dev);
+
+ r = dm_pool_get_metadata_dev_size(pool->pmd, &sb_metadata_dev_size);
+ if (r) {
+ DMERR("failed to retrieve data device size");
+ return r;
+ }
+
+ if (metadata_dev_size < sb_metadata_dev_size) {
+ DMERR("metadata device (%llu sectors) too small: expected %llu",
+ metadata_dev_size, sb_metadata_dev_size);
+ return -EINVAL;
+
+ } else if (metadata_dev_size > sb_metadata_dev_size) {
+ r = dm_pool_resize_metadata_dev(pool->pmd, metadata_dev_size);
+ if (r) {
+ DMERR("failed to resize metadata device");
+ return r;
+ }
+
+ *need_commit = true;
+ }
+
+ return 0;
+}
+
+/*
+ * Retrieves the number of blocks of the data device from
+ * the superblock and compares it to the actual device size,
+ * thus resizing the data device in case it has grown.
+ *
+ * This both copes with opening preallocated data devices in the ctr
+ * being followed by a resume
+ * -and-
+ * calling the resume method individually after userspace has
+ * grown the data device in reaction to a table event.
+ */
+static int pool_preresume(struct dm_target *ti)
+{
+ int r;
+ bool need_commit1, need_commit2;
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+
+ /*
+ * Take control of the pool object.
+ */
+ r = bind_control_target(pool, ti);
+ if (r)
+ return r;
+
+ r = maybe_resize_data_dev(ti, &need_commit1);
+ if (r)
+ return r;
+
+ r = maybe_resize_metadata_dev(ti, &need_commit2);
+ if (r)
+ return r;
+
+ if (need_commit1 || need_commit2)
+ (void) commit_or_fallback(pool);
+
+ return 0;
+}
+
static void pool_resume(struct dm_target *ti)
{
struct pool_c *pt = ti->private;
@@ -2549,7 +2669,7 @@ static struct target_type pool_target = {
.name = "thin-pool",
.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
DM_TARGET_IMMUTABLE,
- .version = {1, 7, 0},
+ .version = {1, 8, 0},
.module = THIS_MODULE,
.ctr = pool_ctr,
.dtr = pool_dtr,
diff --git a/drivers/md/persistent-data/dm-space-map-disk.c b/drivers/md/persistent-data/dm-space-map-disk.c
index f6d29e614ab7..e735a6d5a793 100644
--- a/drivers/md/persistent-data/dm-space-map-disk.c
+++ b/drivers/md/persistent-data/dm-space-map-disk.c
@@ -248,7 +248,8 @@ static struct dm_space_map ops = {
.new_block = sm_disk_new_block,
.commit = sm_disk_commit,
.root_size = sm_disk_root_size,
- .copy_root = sm_disk_copy_root
+ .copy_root = sm_disk_copy_root,
+ .register_threshold_callback = NULL
};
struct dm_space_map *dm_sm_disk_create(struct dm_transaction_manager *tm,
diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c
index 906cf3df71af..1c959684caef 100644
--- a/drivers/md/persistent-data/dm-space-map-metadata.c
+++ b/drivers/md/persistent-data/dm-space-map-metadata.c
@@ -17,6 +17,55 @@
/*----------------------------------------------------------------*/
/*
+ * An edge triggered threshold.
+ */
+struct threshold {
+ bool threshold_set;
+ bool value_set;
+ dm_block_t threshold;
+ dm_block_t current_value;
+ dm_sm_threshold_fn fn;
+ void *context;
+};
+
+static void threshold_init(struct threshold *t)
+{
+ t->threshold_set = false;
+ t->value_set = false;
+}
+
+static void set_threshold(struct threshold *t, dm_block_t value,
+ dm_sm_threshold_fn fn, void *context)
+{
+ t->threshold_set = true;
+ t->threshold = value;
+ t->fn = fn;
+ t->context = context;
+}
+
+static bool below_threshold(struct threshold *t, dm_block_t value)
+{
+ return t->threshold_set && value <= t->threshold;
+}
+
+static bool threshold_already_triggered(struct threshold *t)
+{
+ return t->value_set && below_threshold(t, t->current_value);
+}
+
+static void check_threshold(struct threshold *t, dm_block_t value)
+{
+ if (below_threshold(t, value) &&
+ !threshold_already_triggered(t))
+ t->fn(t->context);
+
+ t->value_set = true;
+ t->current_value = value;
+}
+
+/*----------------------------------------------------------------*/
+
+/*
* Space map interface.
*
* The low level disk format is written using the standard btree and
@@ -54,6 +103,8 @@ struct sm_metadata {
unsigned allocated_this_transaction;
unsigned nr_uncommitted;
struct block_op uncommitted[MAX_RECURSIVE_ALLOCATIONS];
+
+ struct threshold threshold;
};
static int add_bop(struct sm_metadata *smm, enum block_op_type type, dm_block_t b)
@@ -144,12 +195,6 @@ static void sm_metadata_destroy(struct dm_space_map *sm)
kfree(smm);
}
-static int sm_metadata_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
-{
- DMERR("doesn't support extend");
- return -EINVAL;
-}
-
static int sm_metadata_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
{
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
@@ -335,9 +380,19 @@ static int sm_metadata_new_block_(struct dm_space_map *sm, dm_block_t *b)
static int sm_metadata_new_block(struct dm_space_map *sm, dm_block_t *b)
{
+ dm_block_t count;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
int r = sm_metadata_new_block_(sm, b);
if (r)
DMERR("unable to allocate new metadata block");
+
+ r = sm_metadata_get_nr_free(sm, &count);
+ if (r)
+ DMERR("couldn't get free block count");
+
+ check_threshold(&smm->threshold, count);
+
return r;
}
@@ -357,6 +412,18 @@ static int sm_metadata_commit(struct dm_space_map *sm)
return 0;
}
+static int sm_metadata_register_threshold_callback(struct dm_space_map *sm,
+ dm_block_t threshold,
+ dm_sm_threshold_fn fn,
+ void *context)
+{
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+
+ set_threshold(&smm->threshold, threshold, fn, context);
+
+ return 0;
+}
+
static int sm_metadata_root_size(struct dm_space_map *sm, size_t *result)
{
*result = sizeof(struct disk_sm_root);
@@ -382,6 +449,8 @@ static int sm_metadata_copy_root(struct dm_space_map *sm, void *where_le, size_t
return 0;
}
+static int sm_metadata_extend(struct dm_space_map *sm, dm_block_t extra_blocks);
+
static struct dm_space_map ops = {
.destroy = sm_metadata_destroy,
.extend = sm_metadata_extend,
@@ -395,7 +464,8 @@ static struct dm_space_map ops = {
.new_block = sm_metadata_new_block,
.commit = sm_metadata_commit,
.root_size = sm_metadata_root_size,
- .copy_root = sm_metadata_copy_root
+ .copy_root = sm_metadata_copy_root,
+ .register_threshold_callback = sm_metadata_register_threshold_callback
};
/*----------------------------------------------------------------*/
@@ -410,7 +480,7 @@ static void sm_bootstrap_destroy(struct dm_space_map *sm)
static int sm_bootstrap_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
{
- DMERR("boostrap doesn't support extend");
+ DMERR("bootstrap doesn't support extend");
return -EINVAL;
}
@@ -450,7 +520,7 @@ static int sm_bootstrap_count_is_more_than_one(struct dm_space_map *sm,
static int sm_bootstrap_set_count(struct dm_space_map *sm, dm_block_t b,
uint32_t count)
{
- DMERR("boostrap doesn't support set_count");
+ DMERR("bootstrap doesn't support set_count");
return -EINVAL;
}
@@ -491,7 +561,7 @@ static int sm_bootstrap_commit(struct dm_space_map *sm)
static int sm_bootstrap_root_size(struct dm_space_map *sm, size_t *result)
{
- DMERR("boostrap doesn't support root_size");
+ DMERR("bootstrap doesn't support root_size");
return -EINVAL;
}
@@ -499,7 +569,7 @@ static int sm_bootstrap_root_size(struct dm_space_map *sm, size_t *result)
static int sm_bootstrap_copy_root(struct dm_space_map *sm, void *where,
size_t max)
{
- DMERR("boostrap doesn't support copy_root");
+ DMERR("bootstrap doesn't support copy_root");
return -EINVAL;
}
@@ -517,11 +587,42 @@ static struct dm_space_map bootstrap_ops = {
.new_block = sm_bootstrap_new_block,
.commit = sm_bootstrap_commit,
.root_size = sm_bootstrap_root_size,
- .copy_root = sm_bootstrap_copy_root
+ .copy_root = sm_bootstrap_copy_root,
+ .register_threshold_callback = NULL
};
/*----------------------------------------------------------------*/
+static int sm_metadata_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
+{
+ int r, i;
+ enum allocation_event ev;
+ struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
+ dm_block_t old_len = smm->ll.nr_blocks;
+
+ /*
+ * Flick into a mode where all blocks get allocated in the new area.
+ */
+ smm->begin = old_len;
+ memcpy(&smm->sm, &bootstrap_ops, sizeof(smm->sm));
+
+ /*
+ * Extend.
+ */
+ r = sm_ll_extend(&smm->ll, extra_blocks);
+
+ /*
+ * Switch back to normal behaviour.
+ */
+ memcpy(&smm->sm, &ops, sizeof(smm->sm));
+ for (i = old_len; !r && i < smm->begin; i++)
+ r = sm_ll_inc(&smm->ll, i, &ev);
+
+ return r;
+}
+
+/*----------------------------------------------------------------*/
+
struct dm_space_map *dm_sm_metadata_init(void)
{
struct sm_metadata *smm;
@@ -549,6 +650,7 @@ int dm_sm_metadata_create(struct dm_space_map *sm,
smm->recursion_count = 0;
smm->allocated_this_transaction = 0;
smm->nr_uncommitted = 0;
+ threshold_init(&smm->threshold);
memcpy(&smm->sm, &bootstrap_ops, sizeof(smm->sm));
@@ -590,6 +692,7 @@ int dm_sm_metadata_open(struct dm_space_map *sm,
smm->recursion_count = 0;
smm->allocated_this_transaction = 0;
smm->nr_uncommitted = 0;
+ threshold_init(&smm->threshold);
memcpy(&smm->old_ll, &smm->ll, sizeof(smm->old_ll));
return 0;
diff --git a/drivers/md/persistent-data/dm-space-map.h b/drivers/md/persistent-data/dm-space-map.h
index 1cbfc6b1638a..3e6d1153b7c4 100644
--- a/drivers/md/persistent-data/dm-space-map.h
+++ b/drivers/md/persistent-data/dm-space-map.h
@@ -9,6 +9,8 @@
#include "dm-block-manager.h"
+typedef void (*dm_sm_threshold_fn)(void *context);
+
/*
* struct dm_space_map keeps a record of how many times each block in a device
* is referenced. It needs to be fixed on disk as part of the transaction.
@@ -59,6 +61,15 @@ struct dm_space_map {
*/
int (*root_size)(struct dm_space_map *sm, size_t *result);
int (*copy_root)(struct dm_space_map *sm, void *copy_to_here_le, size_t len);
+
+ /*
+ * You can register one threshold callback which is edge-triggered
+ * when the free space in the space map drops below the threshold.
+ */
+ int (*register_threshold_callback)(struct dm_space_map *sm,
+ dm_block_t threshold,
+ dm_sm_threshold_fn fn,
+ void *context);
};
/*----------------------------------------------------------------*/
@@ -131,4 +142,16 @@ static inline int dm_sm_copy_root(struct dm_space_map *sm, void *copy_to_here_le
return sm->copy_root(sm, copy_to_here_le, len);
}
+static inline int dm_sm_register_threshold_callback(struct dm_space_map *sm,
+ dm_block_t threshold,
+ dm_sm_threshold_fn fn,
+ void *context)
+{
+ if (sm->register_threshold_callback)
+ return sm->register_threshold_callback(sm, threshold, fn, context);
+
+ return -EINVAL;
+}
+
+
#endif /* _LINUX_DM_SPACE_MAP_H */
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index f8f0156dff4e..200020eb3005 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -280,5 +280,6 @@ source "drivers/net/wireless/rtlwifi/Kconfig"
source "drivers/net/wireless/ti/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig"
source "drivers/net/wireless/mwifiex/Kconfig"
+source "drivers/net/wireless/cw1200/Kconfig"
endif # WLAN
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 67156efe14c4..0fab227025be 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -57,3 +57,5 @@ obj-$(CONFIG_MWIFIEX) += mwifiex/
obj-$(CONFIG_BRCMFMAC) += brcm80211/
obj-$(CONFIG_BRCMSMAC) += brcm80211/
+
+obj-$(CONFIG_CW1200) += cw1200/
diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig
index 3c2cbc9d6295..760ab3fe09e2 100644
--- a/drivers/net/wireless/ath/ath9k/Kconfig
+++ b/drivers/net/wireless/ath/ath9k/Kconfig
@@ -84,14 +84,6 @@ config ATH9K_DFS_CERTIFIED
developed. At this point enabling this option won't do anything
except increase code size.
-config ATH9K_MAC_DEBUG
- bool "Atheros MAC statistics"
- depends on ATH9K_DEBUGFS
- default y
- ---help---
- This option enables collection of statistics for Rx/Tx status
- data and some other MAC related statistics
-
config ATH9K_LEGACY_RATE_CONTROL
bool "Atheros ath9k rate control"
depends on ATH9K
diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c
index 7ecd40f07a74..e91725bf401c 100644
--- a/drivers/net/wireless/ath/ath9k/ani.c
+++ b/drivers/net/wireless/ath/ath9k/ani.c
@@ -118,10 +118,10 @@ static void ath9k_ani_restart(struct ath_hw *ah)
{
struct ar5416AniState *aniState;
- if (!DO_ANI(ah))
+ if (!ah->curchan)
return;
- aniState = &ah->curchan->ani;
+ aniState = &ah->ani;
aniState->listenTime = 0;
ENABLE_REGWRITE_BUFFER(ah);
@@ -143,7 +143,7 @@ static void ath9k_ani_restart(struct ath_hw *ah)
static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel,
bool scan)
{
- struct ar5416AniState *aniState = &ah->curchan->ani;
+ struct ar5416AniState *aniState = &ah->ani;
struct ath_common *common = ath9k_hw_common(ah);
const struct ani_ofdm_level_entry *entry_ofdm;
const struct ani_cck_level_entry *entry_cck;
@@ -195,10 +195,10 @@ static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
{
struct ar5416AniState *aniState;
- if (!DO_ANI(ah))
+ if (!ah->curchan)
return;
- aniState = &ah->curchan->ani;
+ aniState = &ah->ani;
if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL)
ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1, false);
@@ -210,7 +210,7 @@ static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel,
bool scan)
{
- struct ar5416AniState *aniState = &ah->curchan->ani;
+ struct ar5416AniState *aniState = &ah->ani;
struct ath_common *common = ath9k_hw_common(ah);
const struct ani_ofdm_level_entry *entry_ofdm;
const struct ani_cck_level_entry *entry_cck;
@@ -251,10 +251,10 @@ static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah)
{
struct ar5416AniState *aniState;
- if (!DO_ANI(ah))
+ if (!ah->curchan)
return;
- aniState = &ah->curchan->ani;
+ aniState = &ah->ani;
if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL)
ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1,
@@ -269,7 +269,7 @@ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
{
struct ar5416AniState *aniState;
- aniState = &ah->curchan->ani;
+ aniState = &ah->ani;
/* lower OFDM noise immunity */
if (aniState->ofdmNoiseImmunityLevel > 0 &&
@@ -292,12 +292,12 @@ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
*/
void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
{
- struct ar5416AniState *aniState = &ah->curchan->ani;
+ struct ar5416AniState *aniState = &ah->ani;
struct ath9k_channel *chan = ah->curchan;
struct ath_common *common = ath9k_hw_common(ah);
int ofdm_nil, cck_nil;
- if (!DO_ANI(ah))
+ if (!ah->curchan)
return;
BUG_ON(aniState == NULL);
@@ -380,7 +380,7 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
static bool ath9k_hw_ani_read_counters(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
- struct ar5416AniState *aniState = &ah->curchan->ani;
+ struct ar5416AniState *aniState = &ah->ani;
u32 phyCnt1, phyCnt2;
int32_t listenTime;
@@ -415,10 +415,10 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan)
struct ath_common *common = ath9k_hw_common(ah);
u32 ofdmPhyErrRate, cckPhyErrRate;
- if (!DO_ANI(ah))
+ if (!ah->curchan)
return;
- aniState = &ah->curchan->ani;
+ aniState = &ah->ani;
if (!ath9k_hw_ani_read_counters(ah))
return;
@@ -490,32 +490,22 @@ EXPORT_SYMBOL(ath9k_hw_disable_mib_counters);
void ath9k_hw_ani_init(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
- int i;
+ struct ar5416AniState *ani = &ah->ani;
ath_dbg(common, ANI, "Initialize ANI\n");
ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;
ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW;
-
ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH;
ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW;
- for (i = 0; i < ARRAY_SIZE(ah->channels); i++) {
- struct ath9k_channel *chan = &ah->channels[i];
- struct ar5416AniState *ani = &chan->ani;
-
- ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
-
- ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
-
- ani->mrcCCK = AR_SREV_9300_20_OR_LATER(ah) ? true : false;
-
- ani->ofdmsTurn = true;
-
- ani->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG;
- ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;
- ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL;
- }
+ ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
+ ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
+ ani->mrcCCK = AR_SREV_9300_20_OR_LATER(ah) ? true : false;
+ ani->ofdmsTurn = true;
+ ani->ofdmWeakSigDetect = true;
+ ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;
+ ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL;
/*
* since we expect some ongoing maintenance on the tables, let's sanity
@@ -524,9 +514,6 @@ void ath9k_hw_ani_init(struct ath_hw *ah)
ah->aniperiod = ATH9K_ANI_PERIOD;
ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL;
- if (ah->config.enable_ani)
- ah->proc_phyerr |= HAL_PROCESS_ANI;
-
ath9k_ani_restart(ah);
ath9k_enable_mib_counters(ah);
}
diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h
index dddb1361039a..78b9fa9f6455 100644
--- a/drivers/net/wireless/ath/ath9k/ani.h
+++ b/drivers/net/wireless/ath/ath9k/ani.h
@@ -17,10 +17,6 @@
#ifndef ANI_H
#define ANI_H
-#define HAL_PROCESS_ANI 0x00000001
-
-#define DO_ANI(ah) (((ah)->proc_phyerr & HAL_PROCESS_ANI) && ah->curchan)
-
#define BEACON_RSSI(ahp) (ahp->stats.avgbrssi)
/* units are errors per second */
@@ -38,11 +34,7 @@
#define ATH9K_ANI_CCK_TRIG_LOW 300
#define ATH9K_ANI_NOISE_IMMUNE_LVL 4
-#define ATH9K_ANI_USE_OFDM_WEAK_SIG true
-#define ATH9K_ANI_CCK_WEAK_SIG_THR false
-
#define ATH9K_ANI_SPUR_IMMUNE_LVL 3
-
#define ATH9K_ANI_FIRSTEP_LVL 2
#define ATH9K_ANI_RSSI_THR_HIGH 40
@@ -111,7 +103,7 @@ struct ar5416AniState {
u8 mrcCCK;
u8 spurImmunityLevel;
u8 firstepLevel;
- u8 ofdmWeakSigDetect;
+ bool ofdmWeakSigDetect;
u32 listenTime;
u32 ofdmPhyErrCount;
u32 cckPhyErrCount;
@@ -119,8 +111,6 @@ struct ar5416AniState {
};
struct ar5416Stats {
- u32 ast_ani_niup;
- u32 ast_ani_nidown;
u32 ast_ani_spurup;
u32 ast_ani_spurdown;
u32 ast_ani_ofdmon;
diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
index 391da5ad6a99..d1acfe98918a 100644
--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
@@ -931,7 +931,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah,
{
struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_channel *chan = ah->curchan;
- struct ar5416AniState *aniState = &chan->ani;
+ struct ar5416AniState *aniState = &ah->ani;
s32 value, value2;
switch (cmd & ah->ani_function) {
@@ -1207,7 +1207,7 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_channel *chan = ah->curchan;
- struct ar5416AniState *aniState = &chan->ani;
+ struct ar5416AniState *aniState = &ah->ani;
struct ath9k_ani_default *iniDef;
u32 val;
@@ -1251,7 +1251,7 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah)
/* these levels just got reset to defaults by the INI */
aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
- aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG;
+ aniState->ofdmWeakSigDetect = true;
aniState->mrcCCK = false; /* not available on pre AR9003 */
}
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
index 830daa12feb6..8dc2d089cdef 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
@@ -38,10 +38,6 @@ static int ar9002_hw_init_mode_regs(struct ath_hw *ah)
else
INIT_INI_ARRAY(&ah->iniPcieSerdes,
ar9280PciePhy_clkreq_always_on_L1_9280);
-#ifdef CONFIG_PM_SLEEP
- INIT_INI_ARRAY(&ah->iniPcieSerdesWow,
- ar9280PciePhy_awow);
-#endif
if (AR_SREV_9287_11_OR_LATER(ah)) {
INIT_INI_ARRAY(&ah->iniModes, ar9287Modes_9287_1_1);
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_initvals.h b/drivers/net/wireless/ath/ath9k/ar9002_initvals.h
index beb6162cf97c..4d18c66a6790 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar9002_initvals.h
@@ -925,20 +925,6 @@ static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = {
{0x00004044, 0x00000000},
};
-static const u32 ar9280PciePhy_awow[][2] = {
- /* Addr allmodes */
- {0x00004040, 0x9248fd00},
- {0x00004040, 0x24924924},
- {0x00004040, 0xa8000019},
- {0x00004040, 0x13160820},
- {0x00004040, 0xe5980560},
- {0x00004040, 0xc01dcffd},
- {0x00004040, 0x1aaabe41},
- {0x00004040, 0xbe105554},
- {0x00004040, 0x00043007},
- {0x00004044, 0x00000000},
-};
-
static const u32 ar9285Modes_9285_1_2[][5] = {
/* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
{0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
index 301bf72c53bf..5163abd3937c 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
@@ -469,6 +469,7 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs,
rxs->rs_status = 0;
rxs->rs_flags = 0;
+ rxs->flag = 0;
rxs->rs_datalen = rxsp->status2 & AR_DataLen;
rxs->rs_tstamp = rxsp->status3;
@@ -493,8 +494,8 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs,
rxs->rs_isaggr = (rxsp->status11 & AR_RxAggr) ? 1 : 0;
rxs->rs_moreaggr = (rxsp->status11 & AR_RxMoreAggr) ? 1 : 0;
rxs->rs_antenna = (MS(rxsp->status4, AR_RxAntenna) & 0x7);
- rxs->rs_flags = (rxsp->status4 & AR_GI) ? ATH9K_RX_GI : 0;
- rxs->rs_flags |= (rxsp->status4 & AR_2040) ? ATH9K_RX_2040 : 0;
+ rxs->flag |= (rxsp->status4 & AR_GI) ? RX_FLAG_SHORT_GI : 0;
+ rxs->flag |= (rxsp->status4 & AR_2040) ? RX_FLAG_40MHZ : 0;
rxs->evm0 = rxsp->status6;
rxs->evm1 = rxsp->status7;
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
index 09c1f9da67a0..6343cc91953e 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
@@ -454,6 +454,8 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain)
if (accum_cnt <= thresh_accum_cnt)
continue;
+ max_index++;
+
/* sum(tx amplitude) */
accum_tx = ((data_L[i] >> 16) & 0xffff) |
((data_U[i] & 0x7ff) << 16);
@@ -468,20 +470,21 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain)
accum_tx <<= scale_factor;
accum_rx <<= scale_factor;
- x_est[i + 1] = (((accum_tx + accum_cnt) / accum_cnt) + 32) >>
- scale_factor;
+ x_est[max_index] =
+ (((accum_tx + accum_cnt) / accum_cnt) + 32) >>
+ scale_factor;
- Y[i + 1] = ((((accum_rx + accum_cnt) / accum_cnt) + 32) >>
+ Y[max_index] =
+ ((((accum_rx + accum_cnt) / accum_cnt) + 32) >>
scale_factor) +
- (1 << scale_factor) * max_index + 16;
+ (1 << scale_factor) * i + 16;
if (accum_ang >= (1 << 26))
accum_ang -= 1 << 27;
- theta[i + 1] = ((accum_ang * (1 << scale_factor)) + accum_cnt) /
- accum_cnt;
-
- max_index++;
+ theta[max_index] =
+ ((accum_ang * (1 << scale_factor)) + accum_cnt) /
+ accum_cnt;
}
/*
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index e1714d7c9eeb..83e03857c014 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -905,7 +905,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
{
struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_channel *chan = ah->curchan;
- struct ar5416AniState *aniState = &chan->ani;
+ struct ar5416AniState *aniState = &ah->ani;
s32 value, value2;
switch (cmd & ah->ani_function) {
@@ -1173,7 +1173,7 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah)
struct ath9k_ani_default *iniDef;
u32 val;
- aniState = &ah->curchan->ani;
+ aniState = &ah->ani;
iniDef = &aniState->iniDef;
ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n",
@@ -1214,7 +1214,7 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah)
/* these levels just got reset to defaults by the INI */
aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
- aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG;
+ aniState->ofdmWeakSigDetect = true;
aniState->mrcCCK = true;
}
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 42b03dc39d14..18fcee4e9d68 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -642,6 +642,7 @@ enum sc_op_flags {
SC_OP_ANI_RUN,
SC_OP_PRIM_STA_VIF,
SC_OP_HW_RESET,
+ SC_OP_SCANNING,
};
/* Powersave flags */
@@ -755,7 +756,6 @@ struct ath_softc {
struct rchan *rfs_chan_spec_scan;
enum spectral_mode spectral_mode;
struct ath_spec_scan spec_config;
- int scanning;
#ifdef CONFIG_PM_SLEEP
atomic_t wow_got_bmiss_intr;
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index 2ff570f7f8ff..fd1eebab8647 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -39,7 +39,8 @@ static void ath9k_beaconq_config(struct ath_softc *sc)
ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi);
- if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
+ if (sc->sc_ah->opmode == NL80211_IFTYPE_AP ||
+ sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) {
/* Always burst out beacon and CAB traffic. */
qi.tqi_aifs = 1;
qi.tqi_cwmin = 0;
@@ -273,7 +274,8 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc)
u64 tsf;
int slot;
- if (sc->sc_ah->opmode != NL80211_IFTYPE_AP) {
+ if (sc->sc_ah->opmode != NL80211_IFTYPE_AP &&
+ sc->sc_ah->opmode != NL80211_IFTYPE_MESH_POINT) {
ath_dbg(common, BEACON, "slot 0, tsf: %llu\n",
ath9k_hw_gettsf64(sc->sc_ah));
return 0;
@@ -765,10 +767,10 @@ void ath9k_set_beacon(struct ath_softc *sc)
switch (sc->sc_ah->opmode) {
case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_MESH_POINT:
ath9k_beacon_config_ap(sc, cur_conf);
break;
case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_MESH_POINT:
ath9k_beacon_config_adhoc(sc, cur_conf);
break;
case NL80211_IFTYPE_STATION:
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index b37eb8d38811..2721f52f0177 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -173,25 +173,69 @@ static const struct file_operations fops_rx_chainmask = {
.llseek = default_llseek,
};
-static ssize_t read_file_disable_ani(struct file *file, char __user *user_buf,
+static ssize_t read_file_ani(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- char buf[32];
- unsigned int len;
+ struct ath_hw *ah = sc->sc_ah;
+ unsigned int len = 0, size = 1024;
+ ssize_t retval = 0;
+ char *buf;
- len = sprintf(buf, "%d\n", common->disable_ani);
- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ buf = kzalloc(size, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ if (common->disable_ani) {
+ len += snprintf(buf + len, size - len, "%s: %s\n",
+ "ANI", "DISABLED");
+ goto exit;
+ }
+
+ len += snprintf(buf + len, size - len, "%15s: %s\n",
+ "ANI", "ENABLED");
+ len += snprintf(buf + len, size - len, "%15s: %u\n",
+ "ANI RESET", ah->stats.ast_ani_reset);
+ len += snprintf(buf + len, size - len, "%15s: %u\n",
+ "SPUR UP", ah->stats.ast_ani_spurup);
+ len += snprintf(buf + len, size - len, "%15s: %u\n",
+ "SPUR DOWN", ah->stats.ast_ani_spurup);
+ len += snprintf(buf + len, size - len, "%15s: %u\n",
+ "OFDM WS-DET ON", ah->stats.ast_ani_ofdmon);
+ len += snprintf(buf + len, size - len, "%15s: %u\n",
+ "OFDM WS-DET OFF", ah->stats.ast_ani_ofdmoff);
+ len += snprintf(buf + len, size - len, "%15s: %u\n",
+ "MRC-CCK ON", ah->stats.ast_ani_ccklow);
+ len += snprintf(buf + len, size - len, "%15s: %u\n",
+ "MRC-CCK OFF", ah->stats.ast_ani_cckhigh);
+ len += snprintf(buf + len, size - len, "%15s: %u\n",
+ "FIR-STEP UP", ah->stats.ast_ani_stepup);
+ len += snprintf(buf + len, size - len, "%15s: %u\n",
+ "FIR-STEP DOWN", ah->stats.ast_ani_stepdown);
+ len += snprintf(buf + len, size - len, "%15s: %u\n",
+ "INV LISTENTIME", ah->stats.ast_ani_lneg_or_lzero);
+ len += snprintf(buf + len, size - len, "%15s: %u\n",
+ "OFDM ERRORS", ah->stats.ast_ani_ofdmerrs);
+ len += snprintf(buf + len, size - len, "%15s: %u\n",
+ "CCK ERRORS", ah->stats.ast_ani_cckerrs);
+exit:
+ if (len > size)
+ len = size;
+
+ retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+
+ return retval;
}
-static ssize_t write_file_disable_ani(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
+static ssize_t write_file_ani(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- unsigned long disable_ani;
+ unsigned long ani;
char buf[32];
ssize_t len;
@@ -200,12 +244,15 @@ static ssize_t write_file_disable_ani(struct file *file,
return -EFAULT;
buf[len] = '\0';
- if (strict_strtoul(buf, 0, &disable_ani))
+ if (strict_strtoul(buf, 0, &ani))
return -EINVAL;
- common->disable_ani = !!disable_ani;
+ if (ani < 0 || ani > 1)
+ return -EINVAL;
+
+ common->disable_ani = !ani;
- if (disable_ani) {
+ if (common->disable_ani) {
clear_bit(SC_OP_ANI_RUN, &sc->sc_flags);
ath_stop_ani(sc);
} else {
@@ -215,9 +262,9 @@ static ssize_t write_file_disable_ani(struct file *file,
return count;
}
-static const struct file_operations fops_disable_ani = {
- .read = read_file_disable_ani,
- .write = write_file_disable_ani,
+static const struct file_operations fops_ani = {
+ .read = read_file_ani,
+ .write = write_file_ani,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
@@ -738,8 +785,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
struct ath_tx_status *ts, struct ath_txq *txq,
unsigned int flags)
{
-#define TX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].ts\
- [sc->debug.tsidx].c)
int qnum = txq->axq_qnum;
TX_STAT_INC(qnum, tx_pkts_all);
@@ -771,37 +816,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
TX_STAT_INC(qnum, data_underrun);
if (ts->ts_flags & ATH9K_TX_DELIM_UNDERRUN)
TX_STAT_INC(qnum, delim_underrun);
-
-#ifdef CONFIG_ATH9K_MAC_DEBUG
- spin_lock(&sc->debug.samp_lock);
- TX_SAMP_DBG(jiffies) = jiffies;
- TX_SAMP_DBG(rssi_ctl0) = ts->ts_rssi_ctl0;
- TX_SAMP_DBG(rssi_ctl1) = ts->ts_rssi_ctl1;
- TX_SAMP_DBG(rssi_ctl2) = ts->ts_rssi_ctl2;
- TX_SAMP_DBG(rssi_ext0) = ts->ts_rssi_ext0;
- TX_SAMP_DBG(rssi_ext1) = ts->ts_rssi_ext1;
- TX_SAMP_DBG(rssi_ext2) = ts->ts_rssi_ext2;
- TX_SAMP_DBG(rateindex) = ts->ts_rateindex;
- TX_SAMP_DBG(isok) = !!(ts->ts_status & ATH9K_TXERR_MASK);
- TX_SAMP_DBG(rts_fail_cnt) = ts->ts_shortretry;
- TX_SAMP_DBG(data_fail_cnt) = ts->ts_longretry;
- TX_SAMP_DBG(rssi) = ts->ts_rssi;
- TX_SAMP_DBG(tid) = ts->tid;
- TX_SAMP_DBG(qid) = ts->qid;
-
- if (ts->ts_flags & ATH9K_TX_BA) {
- TX_SAMP_DBG(ba_low) = ts->ba_low;
- TX_SAMP_DBG(ba_high) = ts->ba_high;
- } else {
- TX_SAMP_DBG(ba_low) = 0;
- TX_SAMP_DBG(ba_high) = 0;
- }
-
- sc->debug.tsidx = (sc->debug.tsidx + 1) % ATH_DBG_MAX_SAMPLES;
- spin_unlock(&sc->debug.samp_lock);
-#endif
-
-#undef TX_SAMP_DBG
}
static const struct file_operations fops_xmit = {
@@ -915,8 +929,6 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
{
#define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++
-#define RX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].rs\
- [sc->debug.rsidx].c)
RX_STAT_INC(rx_pkts_all);
sc->debug.stats.rxstats.rx_bytes_all += rs->rs_datalen;
@@ -940,27 +952,7 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
RX_PHY_ERR_INC(rs->rs_phyerr);
}
-#ifdef CONFIG_ATH9K_MAC_DEBUG
- spin_lock(&sc->debug.samp_lock);
- RX_SAMP_DBG(jiffies) = jiffies;
- RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl0;
- RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl1;
- RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl2;
- RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext0;
- RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext1;
- RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext2;
- RX_SAMP_DBG(antenna) = rs->rs_antenna;
- RX_SAMP_DBG(rssi) = rs->rs_rssi;
- RX_SAMP_DBG(rate) = rs->rs_rate;
- RX_SAMP_DBG(is_mybeacon) = rs->is_mybeacon;
-
- sc->debug.rsidx = (sc->debug.rsidx + 1) % ATH_DBG_MAX_SAMPLES;
- spin_unlock(&sc->debug.samp_lock);
-
-#endif
-
#undef RX_PHY_ERR_INC
-#undef RX_SAMP_DBG
}
static const struct file_operations fops_recv = {
@@ -1485,283 +1477,6 @@ static const struct file_operations fops_modal_eeprom = {
.llseek = default_llseek,
};
-#ifdef CONFIG_ATH9K_MAC_DEBUG
-
-void ath9k_debug_samp_bb_mac(struct ath_softc *sc)
-{
-#define ATH_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].c)
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
- unsigned long flags;
- int i;
-
- ath9k_ps_wakeup(sc);
-
- spin_lock_bh(&sc->debug.samp_lock);
-
- spin_lock_irqsave(&common->cc_lock, flags);
- ath_hw_cycle_counters_update(common);
-
- ATH_SAMP_DBG(cc.cycles) = common->cc_ani.cycles;
- ATH_SAMP_DBG(cc.rx_busy) = common->cc_ani.rx_busy;
- ATH_SAMP_DBG(cc.rx_frame) = common->cc_ani.rx_frame;
- ATH_SAMP_DBG(cc.tx_frame) = common->cc_ani.tx_frame;
- spin_unlock_irqrestore(&common->cc_lock, flags);
-
- ATH_SAMP_DBG(noise) = ah->noise;
-
- REG_WRITE_D(ah, AR_MACMISC,
- ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) |
- (AR_MACMISC_MISC_OBS_BUS_1 <<
- AR_MACMISC_MISC_OBS_BUS_MSB_S)));
-
- for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
- ATH_SAMP_DBG(dma_dbg_reg_vals[i]) = REG_READ_D(ah,
- AR_DMADBG_0 + (i * sizeof(u32)));
-
- ATH_SAMP_DBG(pcu_obs) = REG_READ_D(ah, AR_OBS_BUS_1);
- ATH_SAMP_DBG(pcu_cr) = REG_READ_D(ah, AR_CR);
-
- memcpy(ATH_SAMP_DBG(nfCalHist), sc->caldata.nfCalHist,
- sizeof(ATH_SAMP_DBG(nfCalHist)));
-
- sc->debug.sampidx = (sc->debug.sampidx + 1) % ATH_DBG_MAX_SAMPLES;
- spin_unlock_bh(&sc->debug.samp_lock);
- ath9k_ps_restore(sc);
-
-#undef ATH_SAMP_DBG
-}
-
-static int open_file_bb_mac_samps(struct inode *inode, struct file *file)
-{
-#define ATH_SAMP_DBG(c) bb_mac_samp[sampidx].c
- struct ath_softc *sc = inode->i_private;
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
- struct ieee80211_conf *conf = &common->hw->conf;
- struct ath_dbg_bb_mac_samp *bb_mac_samp;
- struct ath9k_nfcal_hist *h;
- int i, j, qcuOffset = 0, dcuOffset = 0;
- u32 *qcuBase, *dcuBase, size = 30000, len = 0;
- u32 sampidx = 0;
- u8 *buf;
- u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
- u8 nread;
-
- if (test_bit(SC_OP_INVALID, &sc->sc_flags))
- return -EAGAIN;
-
- buf = vmalloc(size);
- if (!buf)
- return -ENOMEM;
- bb_mac_samp = vmalloc(sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES);
- if (!bb_mac_samp) {
- vfree(buf);
- return -ENOMEM;
- }
- /* Account the current state too */
- ath9k_debug_samp_bb_mac(sc);
-
- spin_lock_bh(&sc->debug.samp_lock);
- memcpy(bb_mac_samp, sc->debug.bb_mac_samp,
- sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES);
- len += snprintf(buf + len, size - len,
- "Current Sample Index: %d\n", sc->debug.sampidx);
- spin_unlock_bh(&sc->debug.samp_lock);
-
- len += snprintf(buf + len, size - len,
- "Raw DMA Debug Dump:\n");
- len += snprintf(buf + len, size - len, "Sample |\t");
- for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
- len += snprintf(buf + len, size - len, " DMA Reg%d |\t", i);
- len += snprintf(buf + len, size - len, "\n");
-
- for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
- len += snprintf(buf + len, size - len, "%d\t", sampidx);
-
- for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
- len += snprintf(buf + len, size - len, " %08x\t",
- ATH_SAMP_DBG(dma_dbg_reg_vals[i]));
- len += snprintf(buf + len, size - len, "\n");
- }
- len += snprintf(buf + len, size - len, "\n");
-
- len += snprintf(buf + len, size - len,
- "Sample Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n");
- for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
- qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]);
- dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]);
-
- for (i = 0; i < ATH9K_NUM_QUEUES; i++,
- qcuOffset += 4, dcuOffset += 5) {
- if (i == 8) {
- qcuOffset = 0;
- qcuBase++;
- }
-
- if (i == 6) {
- dcuOffset = 0;
- dcuBase++;
- }
- if (!sc->debug.stats.txstats[i].queued)
- continue;
-
- len += snprintf(buf + len, size - len,
- "%4d %7d %2x %1x %2x %2x\n",
- sampidx, i,
- (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
- (*qcuBase & (0x8 << qcuOffset)) >>
- (qcuOffset + 3),
- ATH_SAMP_DBG(dma_dbg_reg_vals[2]) &
- (0x7 << (i * 3)) >> (i * 3),
- (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
- }
- len += snprintf(buf + len, size - len, "\n");
- }
- len += snprintf(buf + len, size - len,
- "samp qcu_sh qcu_fh qcu_comp dcu_comp dcu_arb dcu_fp "
- "ch_idle_dur ch_idle_dur_val txfifo_val0 txfifo_val1 "
- "txfifo_dcu0 txfifo_dcu1 pcu_obs AR_CR\n");
-
- for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
- qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]);
- dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]);
-
- len += snprintf(buf + len, size - len, "%4d %5x %5x ", sampidx,
- (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x003c0000) >> 18,
- (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x03c00000) >> 22);
- len += snprintf(buf + len, size - len, "%7x %8x ",
- (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x1c000000) >> 26,
- (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x3));
- len += snprintf(buf + len, size - len, "%7x %7x ",
- (ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x06000000) >> 25,
- (ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x38000000) >> 27);
- len += snprintf(buf + len, size - len, "%7d %12d ",
- (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x000003fc) >> 2,
- (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000400) >> 10);
- len += snprintf(buf + len, size - len, "%12d %12d ",
- (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000800) >> 11,
- (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00001000) >> 12);
- len += snprintf(buf + len, size - len, "%12d %12d ",
- (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x0001e000) >> 13,
- (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x001e0000) >> 17);
- len += snprintf(buf + len, size - len, "0x%07x 0x%07x\n",
- ATH_SAMP_DBG(pcu_obs), ATH_SAMP_DBG(pcu_cr));
- }
-
- len += snprintf(buf + len, size - len,
- "Sample ChNoise Chain privNF #Reading Readings\n");
- for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
- h = ATH_SAMP_DBG(nfCalHist);
- if (!ATH_SAMP_DBG(noise))
- continue;
-
- for (i = 0; i < NUM_NF_READINGS; i++) {
- if (!(chainmask & (1 << i)) ||
- ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)))
- continue;
-
- nread = AR_PHY_CCA_FILTERWINDOW_LENGTH -
- h[i].invalidNFcount;
- len += snprintf(buf + len, size - len,
- "%4d %5d %4d\t %d\t %d\t",
- sampidx, ATH_SAMP_DBG(noise),
- i, h[i].privNF, nread);
- for (j = 0; j < nread; j++)
- len += snprintf(buf + len, size - len,
- " %d", h[i].nfCalBuffer[j]);
- len += snprintf(buf + len, size - len, "\n");
- }
- }
- len += snprintf(buf + len, size - len, "\nCycle counters:\n"
- "Sample Total Rxbusy Rxframes Txframes\n");
- for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
- if (!ATH_SAMP_DBG(cc.cycles))
- continue;
- len += snprintf(buf + len, size - len,
- "%4d %08x %08x %08x %08x\n",
- sampidx, ATH_SAMP_DBG(cc.cycles),
- ATH_SAMP_DBG(cc.rx_busy),
- ATH_SAMP_DBG(cc.rx_frame),
- ATH_SAMP_DBG(cc.tx_frame));
- }
-
- len += snprintf(buf + len, size - len, "Tx status Dump :\n");
- len += snprintf(buf + len, size - len,
- "Sample rssi:- ctl0 ctl1 ctl2 ext0 ext1 ext2 comb "
- "isok rts_fail data_fail rate tid qid "
- "ba_low ba_high tx_before(ms)\n");
- for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
- for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) {
- if (!ATH_SAMP_DBG(ts[i].jiffies))
- continue;
- len += snprintf(buf + len, size - len, "%-14d"
- "%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-4d %-8d "
- "%-9d %-4d %-3d %-3d %08x %08x %-11d\n",
- sampidx,
- ATH_SAMP_DBG(ts[i].rssi_ctl0),
- ATH_SAMP_DBG(ts[i].rssi_ctl1),
- ATH_SAMP_DBG(ts[i].rssi_ctl2),
- ATH_SAMP_DBG(ts[i].rssi_ext0),
- ATH_SAMP_DBG(ts[i].rssi_ext1),
- ATH_SAMP_DBG(ts[i].rssi_ext2),
- ATH_SAMP_DBG(ts[i].rssi),
- ATH_SAMP_DBG(ts[i].isok),
- ATH_SAMP_DBG(ts[i].rts_fail_cnt),
- ATH_SAMP_DBG(ts[i].data_fail_cnt),
- ATH_SAMP_DBG(ts[i].rateindex),
- ATH_SAMP_DBG(ts[i].tid),
- ATH_SAMP_DBG(ts[i].qid),
- ATH_SAMP_DBG(ts[i].ba_low),
- ATH_SAMP_DBG(ts[i].ba_high),
- jiffies_to_msecs(jiffies -
- ATH_SAMP_DBG(ts[i].jiffies)));
- }
- }
-
- len += snprintf(buf + len, size - len, "Rx status Dump :\n");
- len += snprintf(buf + len, size - len, "Sample rssi:- ctl0 ctl1 ctl2 "
- "ext0 ext1 ext2 comb beacon ant rate rx_before(ms)\n");
- for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
- for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) {
- if (!ATH_SAMP_DBG(rs[i].jiffies))
- continue;
- len += snprintf(buf + len, size - len, "%-14d"
- "%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-9s %-2d %02x %-13d\n",
- sampidx,
- ATH_SAMP_DBG(rs[i].rssi_ctl0),
- ATH_SAMP_DBG(rs[i].rssi_ctl1),
- ATH_SAMP_DBG(rs[i].rssi_ctl2),
- ATH_SAMP_DBG(rs[i].rssi_ext0),
- ATH_SAMP_DBG(rs[i].rssi_ext1),
- ATH_SAMP_DBG(rs[i].rssi_ext2),
- ATH_SAMP_DBG(rs[i].rssi),
- ATH_SAMP_DBG(rs[i].is_mybeacon) ?
- "True" : "False",
- ATH_SAMP_DBG(rs[i].antenna),
- ATH_SAMP_DBG(rs[i].rate),
- jiffies_to_msecs(jiffies -
- ATH_SAMP_DBG(rs[i].jiffies)));
- }
- }
-
- vfree(bb_mac_samp);
- file->private_data = buf;
-
- return 0;
-#undef ATH_SAMP_DBG
-}
-
-static const struct file_operations fops_samps = {
- .open = open_file_bb_mac_samps,
- .read = ath9k_debugfs_read_buf,
- .release = ath9k_debugfs_release_buf,
- .owner = THIS_MODULE,
- .llseek = default_llseek,
-};
-
-#endif
-
#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
static ssize_t read_file_btcoex(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
@@ -2059,8 +1774,8 @@ int ath9k_init_debug(struct ath_hw *ah)
sc->debug.debugfs_phy, sc, &fops_rx_chainmask);
debugfs_create_file("tx_chainmask", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, sc, &fops_tx_chainmask);
- debugfs_create_file("disable_ani", S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, sc, &fops_disable_ani);
+ debugfs_create_file("ani", S_IRUSR | S_IWUSR,
+ sc->debug.debugfs_phy, sc, &fops_ani);
debugfs_create_bool("paprd", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
&sc->sc_ah->config.enable_paprd);
debugfs_create_file("regidx", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
@@ -2095,11 +1810,6 @@ int ath9k_init_debug(struct ath_hw *ah)
debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, sc,
&fops_spectral_fft_period);
-
-#ifdef CONFIG_ATH9K_MAC_DEBUG
- debugfs_create_file("samples", S_IRUSR, sc->debug.debugfs_phy, sc,
- &fops_samps);
-#endif
debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR,
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index 9d49aab8b989..fc679198a0f3 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -251,56 +251,10 @@ struct ath_stats {
u32 reset[__RESET_TYPE_MAX];
};
-#define ATH_DBG_MAX_SAMPLES 10
-struct ath_dbg_bb_mac_samp {
- u32 dma_dbg_reg_vals[ATH9K_NUM_DMA_DEBUG_REGS];
- u32 pcu_obs, pcu_cr, noise;
- struct {
- u64 jiffies;
- int8_t rssi_ctl0;
- int8_t rssi_ctl1;
- int8_t rssi_ctl2;
- int8_t rssi_ext0;
- int8_t rssi_ext1;
- int8_t rssi_ext2;
- int8_t rssi;
- bool isok;
- u8 rts_fail_cnt;
- u8 data_fail_cnt;
- u8 rateindex;
- u8 qid;
- u8 tid;
- u32 ba_low;
- u32 ba_high;
- } ts[ATH_DBG_MAX_SAMPLES];
- struct {
- u64 jiffies;
- int8_t rssi_ctl0;
- int8_t rssi_ctl1;
- int8_t rssi_ctl2;
- int8_t rssi_ext0;
- int8_t rssi_ext1;
- int8_t rssi_ext2;
- int8_t rssi;
- bool is_mybeacon;
- u8 antenna;
- u8 rate;
- } rs[ATH_DBG_MAX_SAMPLES];
- struct ath_cycle_counters cc;
- struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
-};
-
struct ath9k_debug {
struct dentry *debugfs_phy;
u32 regidx;
struct ath_stats stats;
-#ifdef CONFIG_ATH9K_MAC_DEBUG
- spinlock_t samp_lock;
- struct ath_dbg_bb_mac_samp bb_mac_samp[ATH_DBG_MAX_SAMPLES];
- u8 sampidx;
- u8 tsidx;
- u8 rsidx;
-#endif
};
int ath9k_init_debug(struct ath_hw *ah);
@@ -364,17 +318,4 @@ static inline void ath_debug_stat_rx(struct ath_softc *sc,
#endif /* CONFIG_ATH9K_DEBUGFS */
-#ifdef CONFIG_ATH9K_MAC_DEBUG
-
-void ath9k_debug_samp_bb_mac(struct ath_softc *sc);
-
-#else
-
-static inline void ath9k_debug_samp_bb_mac(struct ath_softc *sc)
-{
-}
-
-#endif
-
-
#endif /* DEBUG_H */
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index d3b099d7898b..0085e643132f 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -208,6 +208,9 @@ struct ath9k_htc_target_rx_stats {
case NL80211_IFTYPE_AP: \
_priv->num_ap_vif++; \
break; \
+ case NL80211_IFTYPE_MESH_POINT: \
+ _priv->num_mbss_vif++; \
+ break; \
default: \
break; \
} \
@@ -224,6 +227,9 @@ struct ath9k_htc_target_rx_stats {
case NL80211_IFTYPE_AP: \
_priv->num_ap_vif--; \
break; \
+ case NL80211_IFTYPE_MESH_POINT: \
+ _priv->num_mbss_vif--; \
+ break; \
default: \
break; \
} \
@@ -450,6 +456,7 @@ struct ath9k_htc_priv {
u8 sta_slot;
u8 vif_sta_pos[ATH9K_HTC_MAX_VIF];
u8 num_ibss_vif;
+ u8 num_mbss_vif;
u8 num_sta_vif;
u8 num_sta_assoc_vif;
u8 num_ap_vif;
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
index f13f458dd656..e0c03bd64182 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
@@ -28,7 +28,8 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv)
ath9k_hw_get_txq_props(ah, priv->beaconq, &qi);
- if (priv->ah->opmode == NL80211_IFTYPE_AP) {
+ if (priv->ah->opmode == NL80211_IFTYPE_AP ||
+ priv->ah->opmode == NL80211_IFTYPE_MESH_POINT) {
qi.tqi_aifs = 1;
qi.tqi_cwmin = 0;
qi.tqi_cwmax = 0;
@@ -628,6 +629,7 @@ void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
case NL80211_IFTYPE_ADHOC:
ath9k_htc_beacon_config_adhoc(priv, cur_conf);
break;
+ case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP:
ath9k_htc_beacon_config_ap(priv, cur_conf);
break;
@@ -649,6 +651,7 @@ void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv)
case NL80211_IFTYPE_ADHOC:
ath9k_htc_beacon_config_adhoc(priv, cur_conf);
break;
+ case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP:
ath9k_htc_beacon_config_ap(priv, cur_conf);
break;
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index a47f5e05fc04..59f64367e8ca 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -698,7 +698,8 @@ static const struct ieee80211_iface_limit if_limits[] = {
{ .max = 2, .types = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_P2P_CLIENT) },
{ .max = 2, .types = BIT(NL80211_IFTYPE_AP) |
- BIT(NL80211_IFTYPE_P2P_GO) },
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_MESH_POINT) },
};
static const struct ieee80211_iface_combination if_comb = {
@@ -721,6 +722,7 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK |
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+ IEEE80211_HW_MFP_CAPABLE |
IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING;
hw->wiphy->interface_modes =
@@ -728,7 +730,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_GO) |
- BIT(NL80211_IFTYPE_P2P_CLIENT);
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_MESH_POINT);
hw->wiphy->iface_combinations = &if_comb;
hw->wiphy->n_iface_combinations = 1;
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index 0743a47cef8f..34869c2405aa 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -113,7 +113,9 @@ static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
struct ath9k_htc_priv *priv = data;
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
- if ((vif->type == NL80211_IFTYPE_AP) && bss_conf->enable_beacon)
+ if ((vif->type == NL80211_IFTYPE_AP ||
+ vif->type == NL80211_IFTYPE_MESH_POINT) &&
+ bss_conf->enable_beacon)
priv->reconfig_beacon = true;
if (bss_conf->assoc) {
@@ -180,6 +182,8 @@ static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv)
priv->ah->opmode = NL80211_IFTYPE_ADHOC;
else if (priv->num_ap_vif)
priv->ah->opmode = NL80211_IFTYPE_AP;
+ else if (priv->num_mbss_vif)
+ priv->ah->opmode = NL80211_IFTYPE_MESH_POINT;
else
priv->ah->opmode = NL80211_IFTYPE_STATION;
@@ -810,8 +814,7 @@ void ath9k_htc_ani_work(struct work_struct *work)
}
/* Verify whether we must check ANI */
- if (ah->config.enable_ani &&
- (timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
+ if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
aniflag = true;
common->ani.checkani_timer = timestamp;
}
@@ -841,8 +844,7 @@ set_timer:
* short calibration and long calibration.
*/
cal_interval = ATH_LONG_CALINTERVAL;
- if (ah->config.enable_ani)
- cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
+ cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
if (!common->ani.caldone)
cal_interval = min(cal_interval, (u32)short_cal_interval);
@@ -1052,6 +1054,9 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
case NL80211_IFTYPE_AP:
hvif.opmode = HTC_M_HOSTAP;
break;
+ case NL80211_IFTYPE_MESH_POINT:
+ hvif.opmode = HTC_M_WDS; /* close enough */
+ break;
default:
ath_err(common,
"Interface type %d not yet supported\n", vif->type);
@@ -1084,6 +1089,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
INC_VIF(priv, vif->type);
if ((vif->type == NL80211_IFTYPE_AP) ||
+ (vif->type == NL80211_IFTYPE_MESH_POINT) ||
(vif->type == NL80211_IFTYPE_ADHOC))
ath9k_htc_assign_bslot(priv, vif);
@@ -1134,6 +1140,7 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
DEC_VIF(priv, vif->type);
if ((vif->type == NL80211_IFTYPE_AP) ||
+ vif->type == NL80211_IFTYPE_MESH_POINT ||
(vif->type == NL80211_IFTYPE_ADHOC))
ath9k_htc_remove_bslot(priv, vif);
@@ -1525,9 +1532,10 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) {
/*
* Disable SWBA interrupt only if there are no
- * AP/IBSS interfaces.
+ * concurrent AP/mesh or IBSS interfaces.
*/
- if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) {
+ if ((priv->num_ap_vif + priv->num_mbss_vif <= 1) ||
+ priv->num_ibss_vif) {
ath_dbg(common, CONFIG,
"Beacon disabled for BSS: %pM\n",
bss_conf->bssid);
@@ -1538,12 +1546,15 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_BEACON_INT) {
/*
- * Reset the HW TSF for the first AP interface.
+ * Reset the HW TSF for the first AP or mesh interface.
*/
- if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
- (priv->nvifs == 1) &&
- (priv->num_ap_vif == 1) &&
- (vif->type == NL80211_IFTYPE_AP)) {
+ if (priv->nvifs == 1 &&
+ ((priv->ah->opmode == NL80211_IFTYPE_AP &&
+ vif->type == NL80211_IFTYPE_AP &&
+ priv->num_ap_vif == 1) ||
+ (priv->ah->opmode == NL80211_IFTYPE_MESH_POINT &&
+ vif->type == NL80211_IFTYPE_MESH_POINT &&
+ priv->num_mbss_vif == 1))) {
set_bit(OP_TSF_RESET, &priv->op_flags);
}
ath_dbg(common, CONFIG,
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index 6bd0e92ea2aa..e602c9519709 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -887,7 +887,7 @@ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv)
if (priv->rxfilter & FIF_PSPOLL)
rfilt |= ATH9K_RX_FILTER_PSPOLL;
- if (priv->nvifs > 1)
+ if (priv->nvifs > 1 || priv->rxfilter & FIF_OTHER_BSS)
rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL;
return rfilt;
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 15dfefcf2d0f..d813ab8104d6 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -452,7 +452,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
ah->config.pcie_clock_req = 0;
ah->config.pcie_waen = 0;
ah->config.analog_shiftreg = 1;
- ah->config.enable_ani = true;
for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
ah->config.spurchans[i][0] = AR_NO_SPUR;
@@ -549,8 +548,7 @@ static int ath9k_hw_post_init(struct ath_hw *ah)
ah->eep_ops->get_eeprom_ver(ah),
ah->eep_ops->get_eeprom_rev(ah));
- if (ah->config.enable_ani)
- ath9k_hw_ani_init(ah);
+ ath9k_hw_ani_init(ah);
return 0;
}
@@ -1250,10 +1248,10 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode)
switch (opmode) {
case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_MESH_POINT:
set |= AR_STA_ID1_ADHOC;
REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
break;
+ case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP:
set |= AR_STA_ID1_STA_AP;
/* fall through */
@@ -2255,12 +2253,12 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
switch (ah->opmode) {
case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_MESH_POINT:
REG_SET_BIT(ah, AR_TXCFG,
AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY);
REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon +
TU_TO_USEC(ah->atim_window ? ah->atim_window : 1));
flags |= AR_NDP_TIMER_EN;
+ case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP:
REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon);
REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, next_beacon -
@@ -2604,13 +2602,8 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
pCap->hw_caps |= ATH9K_HW_CAP_RTT;
}
- if (AR_SREV_9280_20_OR_LATER(ah)) {
- pCap->hw_caps |= ATH9K_HW_WOW_DEVICE_CAPABLE |
- ATH9K_HW_WOW_PATTERN_MATCH_EXACT;
-
- if (AR_SREV_9280(ah))
- pCap->hw_caps |= ATH9K_HW_WOW_PATTERN_MATCH_DWORD;
- }
+ if (AR_SREV_9462(ah))
+ pCap->hw_caps |= ATH9K_HW_WOW_DEVICE_CAPABLE;
if (AR_SREV_9300_20_OR_LATER(ah) &&
ah->eep_ops->get_eeprom(ah, EEP_PAPRD))
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index ae3034374bc4..7d259b7dc254 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -246,9 +246,7 @@ enum ath9k_hw_caps {
ATH9K_HW_CAP_MCI = BIT(15),
ATH9K_HW_CAP_DFS = BIT(16),
ATH9K_HW_WOW_DEVICE_CAPABLE = BIT(17),
- ATH9K_HW_WOW_PATTERN_MATCH_EXACT = BIT(18),
- ATH9K_HW_WOW_PATTERN_MATCH_DWORD = BIT(19),
- ATH9K_HW_CAP_PAPRD = BIT(20),
+ ATH9K_HW_CAP_PAPRD = BIT(18),
};
/*
@@ -291,7 +289,6 @@ struct ath9k_ops_config {
u32 ofdm_trig_high;
u32 cck_trig_high;
u32 cck_trig_low;
- u32 enable_ani;
u32 enable_paprd;
int serialize_regmode;
bool rx_intr_mitigation;
@@ -423,7 +420,6 @@ struct ath9k_hw_cal_data {
struct ath9k_channel {
struct ieee80211_channel *chan;
- struct ar5416AniState ani;
u16 channel;
u32 channelFlags;
u32 chanmode;
@@ -854,10 +850,10 @@ struct ath_hw {
u32 globaltxtimeout;
/* ANI */
- u32 proc_phyerr;
u32 aniperiod;
enum ath9k_ani_cmd ani_function;
u32 ani_skip_count;
+ struct ar5416AniState ani;
#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
struct ath_btcoex_hw btcoex_hw;
@@ -882,9 +878,6 @@ struct ath_hw {
struct ar5416IniArray iniBank6;
struct ar5416IniArray iniAddac;
struct ar5416IniArray iniPcieSerdes;
-#ifdef CONFIG_PM_SLEEP
- struct ar5416IniArray iniPcieSerdesWow;
-#endif
struct ar5416IniArray iniPcieSerdesLowPower;
struct ar5416IniArray iniModesFastClock;
struct ar5416IniArray iniAdditional;
@@ -1165,8 +1158,6 @@ static inline void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
}
#endif
-
-
#define ATH9K_CLOCK_RATE_CCK 22
#define ATH9K_CLOCK_RATE_5GHZ_OFDM 40
#define ATH9K_CLOCK_RATE_2GHZ_OFDM 44
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 2ba494567777..389ee1b59976 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -21,6 +21,7 @@
#include <linux/ath9k_platform.h>
#include <linux/module.h>
#include <linux/relay.h>
+#include <net/ieee80211_radiotap.h>
#include "ath9k.h"
@@ -613,9 +614,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
spin_lock_init(&sc->sc_serial_rw);
spin_lock_init(&sc->sc_pm_lock);
mutex_init(&sc->mutex);
-#ifdef CONFIG_ATH9K_MAC_DEBUG
- spin_lock_init(&sc->debug.samp_lock);
-#endif
tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet,
(unsigned long)sc);
@@ -769,12 +767,19 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
IEEE80211_HW_SUPPORTS_RC_TABLE;
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT)
- hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
+ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
+ hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
+
+ if (AR_SREV_9280_20_OR_LATER(ah))
+ hw->radiotap_mcs_details |=
+ IEEE80211_RADIOTAP_MCS_HAVE_STBC;
+ }
if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt)
hw->flags |= IEEE80211_HW_MFP_CAPABLE;
+ hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
+
hw->wiphy->interface_modes =
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_CLIENT) |
@@ -794,21 +799,17 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
#ifdef CONFIG_PM_SLEEP
-
if ((ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) &&
device_can_wakeup(sc->dev)) {
-
hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
WIPHY_WOWLAN_DISCONNECT;
hw->wiphy->wowlan.n_patterns = MAX_NUM_USER_PATTERN;
hw->wiphy->wowlan.pattern_min_len = 1;
hw->wiphy->wowlan.pattern_max_len = MAX_PATTERN_SIZE;
-
}
atomic_set(&sc->wow_sleep_proc_intr, -1);
atomic_set(&sc->wow_got_bmiss_intr, -1);
-
#endif
hw->queues = 4;
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
index 849259b07370..fff5d3ccc663 100644
--- a/drivers/net/wireless/ath/ath9k/link.c
+++ b/drivers/net/wireless/ath/ath9k/link.c
@@ -390,9 +390,7 @@ void ath_ani_calibrate(unsigned long data)
}
/* Verify whether we must check ANI */
- if (sc->sc_ah->config.enable_ani
- && (timestamp - common->ani.checkani_timer) >=
- ah->config.ani_poll_interval) {
+ if ((timestamp - common->ani.checkani_timer) >= ah->config.ani_poll_interval) {
aniflag = true;
common->ani.checkani_timer = timestamp;
}
@@ -418,7 +416,6 @@ void ath_ani_calibrate(unsigned long data)
longcal ? "long" : "", shortcal ? "short" : "",
aniflag ? "ani" : "", common->ani.caldone ? "true" : "false");
- ath9k_debug_samp_bb_mac(sc);
ath9k_ps_restore(sc);
set_timer:
@@ -428,9 +425,7 @@ set_timer:
* short calibration and long calibration.
*/
cal_interval = ATH_LONG_CALINTERVAL;
- if (sc->sc_ah->config.enable_ani)
- cal_interval = min(cal_interval,
- (u32)ah->config.ani_poll_interval);
+ cal_interval = min(cal_interval, (u32)ah->config.ani_poll_interval);
if (!common->ani.caldone)
cal_interval = min(cal_interval, (u32)short_cal_interval);
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 566109a40fb3..2ef05ebffbcf 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -547,6 +547,7 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
rs->rs_status = 0;
rs->rs_flags = 0;
+ rs->flag = 0;
rs->rs_datalen = ads.ds_rxstatus1 & AR_DataLen;
rs->rs_tstamp = ads.AR_RcvTimestamp;
@@ -586,10 +587,17 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
rs->rs_moreaggr =
(ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0;
rs->rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna);
- rs->rs_flags =
- (ads.ds_rxstatus3 & AR_GI) ? ATH9K_RX_GI : 0;
- rs->rs_flags |=
- (ads.ds_rxstatus3 & AR_2040) ? ATH9K_RX_2040 : 0;
+
+ /* directly mapped flags for ieee80211_rx_status */
+ rs->flag |=
+ (ads.ds_rxstatus3 & AR_GI) ? RX_FLAG_SHORT_GI : 0;
+ rs->flag |=
+ (ads.ds_rxstatus3 & AR_2040) ? RX_FLAG_40MHZ : 0;
+ if (AR_SREV_9280_20_OR_LATER(ah))
+ rs->flag |=
+ (ads.ds_rxstatus3 & AR_STBC) ?
+ /* we can only Nss=1 STBC */
+ (1 << RX_FLAG_STBC_SHIFT) : 0;
if (ads.ds_rxstatus8 & AR_PreDelimCRCErr)
rs->rs_flags |= ATH9K_RX_DELIM_CRC_PRE;
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index 5865f92998e1..b02dfce964b4 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -149,6 +149,7 @@ struct ath_rx_status {
u32 evm2;
u32 evm3;
u32 evm4;
+ u32 flag; /* see enum mac80211_rx_flags */
};
struct ath_htc_rx_status {
@@ -533,7 +534,8 @@ struct ar5416_desc {
#define AR_2040 0x00000002
#define AR_Parallel40 0x00000004
#define AR_Parallel40_S 2
-#define AR_RxStatusRsvd30 0x000000f8
+#define AR_STBC 0x00000008 /* on ar9280 and later */
+#define AR_RxStatusRsvd30 0x000000f0
#define AR_RxAntenna 0xffffff00
#define AR_RxAntenna_S 8
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 5092ecae7706..e5b186b04b29 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -193,7 +193,6 @@ static bool ath_prepare_reset(struct ath_softc *sc)
ath_stop_ani(sc);
del_timer_sync(&sc->rx_poll_timer);
- ath9k_debug_samp_bb_mac(sc);
ath9k_hw_disable_interrupts(ah);
if (!ath_drain_all_txq(sc))
@@ -1273,7 +1272,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
curchan->center_freq);
} else {
/* perform spectral scan if requested. */
- if (sc->scanning &&
+ if (test_bit(SC_OP_SCANNING, &sc->sc_flags) &&
sc->spectral_mode == SPECTRAL_CHANSCAN)
ath9k_spectral_scan_trigger(hw);
}
@@ -1690,7 +1689,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
bool flush = false;
int ret = 0;
- local_bh_disable();
+ mutex_lock(&sc->mutex);
switch (action) {
case IEEE80211_AMPDU_RX_START:
@@ -1723,7 +1722,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
ath_err(ath9k_hw_common(sc->sc_ah), "Unknown AMPDU action\n");
}
- local_bh_enable();
+ mutex_unlock(&sc->mutex);
return ret;
}
@@ -2007,7 +2006,6 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
- struct ath9k_hw_capabilities *pcaps = &ah->caps;
int pattern_count = 0;
int i, byte_cnt;
u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
@@ -2077,36 +2075,9 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
/* Create Disassociate pattern mask */
- if (pcaps->hw_caps & ATH9K_HW_WOW_PATTERN_MATCH_EXACT) {
-
- if (pcaps->hw_caps & ATH9K_HW_WOW_PATTERN_MATCH_DWORD) {
- /*
- * for AR9280, because of hardware limitation, the
- * first 4 bytes have to be matched for all patterns.
- * the mask for disassociation and de-auth pattern
- * matching need to enable the first 4 bytes.
- * also the duration field needs to be filled.
- */
- dis_deauth_mask[0] = 0xf0;
-
- /*
- * fill in duration field
- FIXME: what is the exact value ?
- */
- dis_deauth_pattern[2] = 0xff;
- dis_deauth_pattern[3] = 0xff;
- } else {
- dis_deauth_mask[0] = 0xfe;
- }
-
- dis_deauth_mask[1] = 0x03;
- dis_deauth_mask[2] = 0xc0;
- } else {
- dis_deauth_mask[0] = 0xef;
- dis_deauth_mask[1] = 0x3f;
- dis_deauth_mask[2] = 0x00;
- dis_deauth_mask[3] = 0xfc;
- }
+ dis_deauth_mask[0] = 0xfe;
+ dis_deauth_mask[1] = 0x03;
+ dis_deauth_mask[2] = 0xc0;
ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n");
@@ -2342,15 +2313,13 @@ static void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
-
- sc->scanning = 1;
+ set_bit(SC_OP_SCANNING, &sc->sc_flags);
}
static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
-
- sc->scanning = 0;
+ clear_bit(SC_OP_SCANNING, &sc->sc_flags);
}
struct ieee80211_ops ath9k_ops = {
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 8be2b5d8c155..865e043e8aa6 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -868,10 +868,7 @@ static int ath9k_process_rate(struct ath_common *common,
if (rx_stats->rs_rate & 0x80) {
/* HT rate */
rxs->flag |= RX_FLAG_HT;
- if (rx_stats->rs_flags & ATH9K_RX_2040)
- rxs->flag |= RX_FLAG_40MHZ;
- if (rx_stats->rs_flags & ATH9K_RX_GI)
- rxs->flag |= RX_FLAG_SHORT_GI;
+ rxs->flag |= rx_stats->flag;
rxs->rate_idx = rx_stats->rs_rate & 0x7f;
return 0;
}
@@ -958,11 +955,11 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
if (rx_stats->rs_more)
return 0;
- ath9k_process_rssi(common, hw, hdr, rx_stats);
-
if (ath9k_process_rate(common, hw, rx_stats, rx_status))
return -EINVAL;
+ ath9k_process_rssi(common, hw, hdr, rx_stats);
+
rx_status->band = hw->conf.chandef.chan->band;
rx_status->freq = hw->conf.chandef.chan->center_freq;
rx_status->signal = ah->noise + rx_stats->rs_rssi;
diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c
index 9f8563091bea..81c88dd606dc 100644
--- a/drivers/net/wireless/ath/ath9k/wow.c
+++ b/drivers/net/wireless/ath/ath9k/wow.c
@@ -34,17 +34,6 @@ const char *ath9k_hw_wow_event_to_string(u32 wow_event)
}
EXPORT_SYMBOL(ath9k_hw_wow_event_to_string);
-static void ath9k_hw_config_serdes_wow_sleep(struct ath_hw *ah)
-{
- int i;
-
- for (i = 0; i < ah->iniPcieSerdesWow.ia_rows; i++)
- REG_WRITE(ah, INI_RA(&ah->iniPcieSerdesWow, i, 0),
- INI_RA(&ah->iniPcieSerdesWow, i, 1));
-
- usleep_range(1000, 1500);
-}
-
static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
@@ -58,15 +47,8 @@ static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah)
ath_err(common, "Failed to stop Rx DMA in 10ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n",
REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW));
return;
- } else {
- if (!AR_SREV_9300_20_OR_LATER(ah))
- REG_WRITE(ah, AR_RXDP, 0x0);
}
- /* AR9280 WoW has sleep issue, do not set it to sleep */
- if (AR_SREV_9280_20(ah))
- return;
-
REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT);
}
@@ -84,27 +66,16 @@ static void ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah)
/* set the transmit buffer */
ctl[0] = (KAL_FRAME_LEN | (MAX_RATE_POWER << 16));
-
- if (!(AR_SREV_9300_20_OR_LATER(ah)))
- ctl[0] += (KAL_ANTENNA_MODE << 25);
-
ctl[1] = 0;
ctl[3] = 0xb; /* OFDM_6M hardware value for this rate */
ctl[4] = 0;
ctl[7] = (ah->txchainmask) << 2;
-
- if (AR_SREV_9300_20_OR_LATER(ah))
- ctl[2] = 0xf << 16; /* tx_tries 0 */
- else
- ctl[2] = 0x7 << 16; /* tx_tries 0 */
-
+ ctl[2] = 0xf << 16; /* tx_tries 0 */
for (i = 0; i < KAL_NUM_DESC_WORDS; i++)
REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
- /* for AR9300 family 13 descriptor words */
- if (AR_SREV_9300_20_OR_LATER(ah))
- REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
+ REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
data_word[0] = (KAL_FRAME_TYPE << 2) | (KAL_FRAME_SUB_TYPE << 4) |
(KAL_TO_DS << 8) | (KAL_DURATION_ID << 16);
@@ -183,9 +154,6 @@ void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
ah->wow_event_mask |= BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT);
- if (!AR_SREV_9285_12_OR_LATER(ah))
- return;
-
if (pattern_count < 4) {
/* Pattern 0-3 uses AR_WOW_LENGTH1 register */
set = (pattern_len & AR_WOW_LENGTH_MAX) <<
@@ -207,6 +175,7 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
{
u32 wow_status = 0;
u32 val = 0, rval;
+
/*
* read the WoW status register to know
* the wakeup reason
@@ -223,19 +192,14 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
val &= ah->wow_event_mask;
if (val) {
-
if (val & AR_WOW_MAGIC_PAT_FOUND)
wow_status |= AH_WOW_MAGIC_PATTERN_EN;
-
if (AR_WOW_PATTERN_FOUND(val))
wow_status |= AH_WOW_USER_PATTERN_EN;
-
if (val & AR_WOW_KEEP_ALIVE_FAIL)
wow_status |= AH_WOW_LINK_CHANGE;
-
if (val & AR_WOW_BEACON_FAIL)
wow_status |= AH_WOW_BEACON_MISS;
-
}
/*
@@ -255,17 +219,6 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN)));
/*
- * tie reset register for AR9002 family of chipsets
- * NB: not tieing it back might have some repurcussions.
- */
-
- if (!AR_SREV_9300_20_OR_LATER(ah)) {
- REG_SET_BIT(ah, AR_WA, AR_WA_UNTIE_RESET_EN |
- AR_WA_POR_SHORT | AR_WA_RESET_EN);
- }
-
-
- /*
* restore the beacon threshold to init value
*/
REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR);
@@ -277,8 +230,7 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
* reset to our Chip's Power On Reset so that any PCI-E
* reset from the bus will not reset our chip
*/
-
- if (AR_SREV_9280_20_OR_LATER(ah) && ah->is_pciexpress)
+ if (ah->is_pciexpress)
ath9k_hw_configpcipowersave(ah, false);
ah->wow_event_mask = 0;
@@ -298,7 +250,6 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
* are from the 'pattern_enable' in this function and
* 'pattern_count' of ath9k_hw_wow_apply_pattern()
*/
-
wow_event_mask = ah->wow_event_mask;
/*
@@ -306,50 +257,15 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
* WOW sleep, we do want the Reset from the PCI-E to disturb
* our hw state
*/
-
if (ah->is_pciexpress) {
-
/*
* we need to untie the internal POR (power-on-reset)
* to the external PCI-E reset. We also need to tie
* the PCI-E Phy reset to the PCI-E reset.
*/
-
- if (AR_SREV_9300_20_OR_LATER(ah)) {
- set = AR_WA_RESET_EN | AR_WA_POR_SHORT;
- clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE;
- REG_RMW(ah, AR_WA, set, clr);
- } else {
- if (AR_SREV_9285(ah) || AR_SREV_9287(ah))
- set = AR9285_WA_DEFAULT;
- else
- set = AR9280_WA_DEFAULT;
-
- /*
- * In AR9280 and AR9285, bit 14 in WA register
- * (disable L1) should only be set when device
- * enters D3 state and be cleared when device
- * comes back to D0
- */
-
- if (ah->config.pcie_waen & AR_WA_D3_L1_DISABLE)
- set |= AR_WA_D3_L1_DISABLE;
-
- clr = AR_WA_UNTIE_RESET_EN;
- set |= AR_WA_RESET_EN | AR_WA_POR_SHORT;
- REG_RMW(ah, AR_WA, set, clr);
-
- /*
- * for WoW sleep, we reprogram the SerDes so that the
- * PLL and CLK REQ are both enabled. This uses more
- * power but otherwise WoW sleep is unstable and the
- * chip may disappear.
- */
-
- if (AR_SREV_9285_12_OR_LATER(ah))
- ath9k_hw_config_serdes_wow_sleep(ah);
-
- }
+ set = AR_WA_RESET_EN | AR_WA_POR_SHORT;
+ clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE;
+ REG_RMW(ah, AR_WA, set, clr);
}
/*
@@ -378,7 +294,6 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
* Program default values for pattern backoff, aifs/slot/KAL count,
* beacon miss timeout, KAL timeout, etc.
*/
-
set = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF);
REG_SET_BIT(ah, AR_WOW_PATTERN, set);
@@ -398,7 +313,7 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
/*
* Keep alive timo in ms except AR9280
*/
- if (!pattern_enable || AR_SREV_9280(ah))
+ if (!pattern_enable)
set = AR_WOW_KEEP_ALIVE_NEVER;
else
set = KAL_TIMEOUT * 32;
@@ -420,7 +335,6 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
/*
* Configure MAC WoW Registers
*/
-
set = 0;
/* Send keep alive timeouts anyway */
clr = AR_WOW_KEEP_ALIVE_AUTO_DIS;
@@ -430,16 +344,9 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
else
set = AR_WOW_KEEP_ALIVE_FAIL_DIS;
- /*
- * FIXME: For now disable keep alive frame
- * failure. This seems to sometimes trigger
- * unnecessary wake up with AR9485 chipsets.
- */
set = AR_WOW_KEEP_ALIVE_FAIL_DIS;
-
REG_RMW(ah, AR_WOW_KEEP_ALIVE, set, clr);
-
/*
* we are relying on a bmiss failure. ensure we have
* enough threshold to prevent false positives
@@ -473,14 +380,8 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
set |= AR_WOW_MAC_INTR_EN;
REG_RMW(ah, AR_WOW_PATTERN, set, clr);
- /*
- * For AR9285 and later version of chipsets
- * enable WoW pattern match for packets less
- * than 256 bytes for all patterns
- */
- if (AR_SREV_9285_12_OR_LATER(ah))
- REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B,
- AR_WOW_PATTERN_SUPPORTED);
+ REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B,
+ AR_WOW_PATTERN_SUPPORTED);
/*
* Set the power states appropriately and enable PME
@@ -488,43 +389,32 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
clr = 0;
set = AR_PMCTRL_PWR_STATE_D1D3 | AR_PMCTRL_HOST_PME_EN |
AR_PMCTRL_PWR_PM_CTRL_ENA;
- /*
- * This is needed for AR9300 chipsets to wake-up
- * the host.
- */
- if (AR_SREV_9300_20_OR_LATER(ah))
- clr = AR_PCIE_PM_CTRL_ENA;
+ clr = AR_PCIE_PM_CTRL_ENA;
REG_RMW(ah, AR_PCIE_PM_CTRL, set, clr);
- if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
- /*
- * this is needed to prevent the chip waking up
- * the host within 3-4 seconds with certain
- * platform/BIOS. The fix is to enable
- * D1 & D3 to match original definition and
- * also match the OTP value. Anyway this
- * is more related to SW WOW.
- */
- clr = AR_PMCTRL_PWR_STATE_D1D3;
- REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr);
-
- set = AR_PMCTRL_PWR_STATE_D1D3_REAL;
- REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set);
- }
-
+ /*
+ * this is needed to prevent the chip waking up
+ * the host within 3-4 seconds with certain
+ * platform/BIOS. The fix is to enable
+ * D1 & D3 to match original definition and
+ * also match the OTP value. Anyway this
+ * is more related to SW WOW.
+ */
+ clr = AR_PMCTRL_PWR_STATE_D1D3;
+ REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr);
+ set = AR_PMCTRL_PWR_STATE_D1D3_REAL;
+ REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set);
REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM);
- if (AR_SREV_9300_20_OR_LATER(ah)) {
- /* to bring down WOW power low margin */
- set = BIT(13);
- REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set);
- /* HW WoW */
- clr = BIT(5);
- REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr);
- }
+ /* to bring down WOW power low margin */
+ set = BIT(13);
+ REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set);
+ /* HW WoW */
+ clr = BIT(5);
+ REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr);
ath9k_hw_set_powermode_wow_sleep(ah);
ah->wow_event_mask = wow_event_mask;
diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig
index bac3d98a0cfb..5644ac54facc 100644
--- a/drivers/net/wireless/ath/wil6210/Kconfig
+++ b/drivers/net/wireless/ath/wil6210/Kconfig
@@ -27,3 +27,15 @@ config WIL6210_ISR_COR
self-clear when accessed for debug purposes, it makes
such monitoring impossible.
Say y unless you debug interrupts
+
+config ATH6KL_TRACING
+ bool "wil6210 tracing support"
+ depends on WIL6210
+ depends on EVENT_TRACING
+ default y
+ ---help---
+ Say Y here to enable tracepoints for the wil6210 driver
+ using the kernel tracing infrastructure. Select this
+ option if you are interested in debugging the driver.
+
+ If unsure, say Y to make it easier to debug problems.
diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile
index d288eea0a26a..f891d514d881 100644
--- a/drivers/net/wireless/ath/wil6210/Makefile
+++ b/drivers/net/wireless/ath/wil6210/Makefile
@@ -1,15 +1,20 @@
obj-$(CONFIG_WIL6210) += wil6210.o
-wil6210-objs := main.o
-wil6210-objs += netdev.o
-wil6210-objs += cfg80211.o
-wil6210-objs += pcie_bus.o
-wil6210-objs += debugfs.o
-wil6210-objs += wmi.o
-wil6210-objs += interrupt.o
-wil6210-objs += txrx.o
+wil6210-y := main.o
+wil6210-y += netdev.o
+wil6210-y += cfg80211.o
+wil6210-y += pcie_bus.o
+wil6210-y += debugfs.o
+wil6210-y += wmi.o
+wil6210-y += interrupt.o
+wil6210-y += txrx.o
+wil6210-y += debug.o
+wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
subdir-ccflags-y += -Werror
endif
+# for tracing framework to find trace.h
+CFLAGS_trace.o := -I$(src)
+
subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index c5d4a87abaaf..4eb05d0818c3 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -322,12 +322,16 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
* FW don't support scan after connection attempt
*/
set_bit(wil_status_dontscan, &wil->status);
+ set_bit(wil_status_fwconnecting, &wil->status);
rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
if (rc == 0) {
/* Connect can take lots of time */
mod_timer(&wil->connect_timer,
jiffies + msecs_to_jiffies(2000));
+ } else {
+ clear_bit(wil_status_dontscan, &wil->status);
+ clear_bit(wil_status_fwconnecting, &wil->status);
}
out:
diff --git a/drivers/net/wireless/ath/wil6210/debug.c b/drivers/net/wireless/ath/wil6210/debug.c
new file mode 100644
index 000000000000..9eeabf4a5879
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/debug.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "wil6210.h"
+#include "trace.h"
+
+int wil_err(struct wil6210_priv *wil, const char *fmt, ...)
+{
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+ int ret;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ ret = netdev_err(ndev, "%pV", &vaf);
+ trace_wil6210_log_err(&vaf);
+ va_end(args);
+
+ return ret;
+}
+
+int wil_info(struct wil6210_priv *wil, const char *fmt, ...)
+{
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+ int ret;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ ret = netdev_info(ndev, "%pV", &vaf);
+ trace_wil6210_log_info(&vaf);
+ va_end(args);
+
+ return ret;
+}
+
+int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ trace_wil6210_log_dbg(&vaf);
+ va_end(args);
+
+ return 0;
+}
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 727b1f53e6ad..e8308ec30970 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -418,9 +418,15 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
if (skb) {
unsigned char printbuf[16 * 3 + 2];
int i = 0;
- int len = skb_headlen(skb);
+ int len = le16_to_cpu(d->dma.length);
void *p = skb->data;
+ if (len != skb_headlen(skb)) {
+ seq_printf(s, "!!! len: desc = %d skb = %d\n",
+ len, skb_headlen(skb));
+ len = min_t(int, len, skb_headlen(skb));
+ }
+
seq_printf(s, " len = %d\n", len);
while (i < len) {
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index e3c1e7684f9c..8205d3e4ab66 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -17,6 +17,7 @@
#include <linux/interrupt.h>
#include "wil6210.h"
+#include "trace.h"
/**
* Theory of operation:
@@ -103,14 +104,14 @@ static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil)
clear_bit(wil_status_irqen, &wil->status);
}
-static void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
+void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
{
iowrite32(WIL6210_IMC_TX, wil->csr +
HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, IMC));
}
-static void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
+void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
{
iowrite32(WIL6210_IMC_RX, wil->csr +
HOSTADDR(RGF_DMA_EP_RX_ICR) +
@@ -168,6 +169,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_RX_ICR) +
offsetof(struct RGF_ICR, ICR));
+ trace_wil6210_irq_rx(isr);
wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);
if (!isr) {
@@ -180,13 +182,14 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
wil_dbg_irq(wil, "RX done\n");
isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
- wil_rx_handle(wil);
+ wil_dbg_txrx(wil, "NAPI schedule\n");
+ napi_schedule(&wil->napi_rx);
}
if (isr)
wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr);
- wil6210_unmask_irq_rx(wil);
+ /* Rx IRQ will be enabled when NAPI processing finished */
return IRQ_HANDLED;
}
@@ -198,6 +201,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, ICR));
+ trace_wil6210_irq_tx(isr);
wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
if (!isr) {
@@ -208,23 +212,17 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
wil6210_mask_irq_tx(wil);
if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {
- uint i;
wil_dbg_irq(wil, "TX done\n");
+ napi_schedule(&wil->napi_tx);
isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
- for (i = 0; i < 24; i++) {
- u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i);
- if (isr & mask) {
- isr &= ~mask;
- wil_dbg_irq(wil, "TX done(%i)\n", i);
- wil_tx_complete(wil, i);
- }
- }
+ /* clear also all VRING interrupts */
+ isr &= ~(BIT(25) - 1UL);
}
if (isr)
wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr);
- wil6210_unmask_irq_tx(wil);
+ /* Tx IRQ will be enabled when NAPI processing finished */
return IRQ_HANDLED;
}
@@ -256,6 +254,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_MISC_ICR) +
offsetof(struct RGF_ICR, ICR));
+ trace_wil6210_irq_misc(isr);
wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr);
if (!isr) {
@@ -301,6 +300,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
struct wil6210_priv *wil = cookie;
u32 isr = wil->isr_misc;
+ trace_wil6210_irq_misc_thread(isr);
wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr);
if (isr & ISR_MISC_FW_ERROR) {
@@ -408,6 +408,7 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie)
if (wil6210_debug_irq_mask(wil, pseudo_cause))
return IRQ_NONE;
+ trace_wil6210_irq_pseudo(pseudo_cause);
wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause);
wil6210_mask_irq_pseudo(wil);
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index a0478e2f6868..c97b864667c5 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -56,27 +56,21 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
{
uint i;
struct net_device *ndev = wil_to_ndev(wil);
- struct wireless_dev *wdev = wil->wdev;
wil_dbg_misc(wil, "%s()\n", __func__);
wil_link_off(wil);
- clear_bit(wil_status_fwconnected, &wil->status);
-
- switch (wdev->sme_state) {
- case CFG80211_SME_CONNECTED:
- cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE,
+ if (test_bit(wil_status_fwconnected, &wil->status)) {
+ clear_bit(wil_status_fwconnected, &wil->status);
+ cfg80211_disconnected(ndev,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
NULL, 0, GFP_KERNEL);
- break;
- case CFG80211_SME_CONNECTING:
+ } else if (test_bit(wil_status_fwconnecting, &wil->status)) {
cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
GFP_KERNEL);
- break;
- default:
- break;
}
-
+ clear_bit(wil_status_fwconnecting, &wil->status);
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++)
wil_vring_fini_tx(wil, i);
@@ -365,6 +359,9 @@ static int __wil_up(struct wil6210_priv *wil)
/* Rx VRING. After MAC and beacon */
wil_rx_init(wil);
+ napi_enable(&wil->napi_rx);
+ napi_enable(&wil->napi_tx);
+
return 0;
}
@@ -381,6 +378,9 @@ int wil_up(struct wil6210_priv *wil)
static int __wil_down(struct wil6210_priv *wil)
{
+ napi_disable(&wil->napi_rx);
+ napi_disable(&wil->napi_tx);
+
if (wil->scan_request) {
cfg80211_scan_done(wil->scan_request, true);
wil->scan_request = NULL;
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index 098a8ec6b841..29dd1e58cb17 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -40,6 +40,55 @@ static const struct net_device_ops wil_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
};
+static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget)
+{
+ struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
+ napi_rx);
+ int quota = budget;
+ int done;
+
+ wil_rx_handle(wil, &quota);
+ done = budget - quota;
+
+ if (done <= 1) { /* burst ends - only one packet processed */
+ napi_complete(napi);
+ wil6210_unmask_irq_rx(wil);
+ wil_dbg_txrx(wil, "NAPI RX complete\n");
+ }
+
+ wil_dbg_txrx(wil, "NAPI RX poll(%d) done %d\n", budget, done);
+
+ return done;
+}
+
+static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget)
+{
+ struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
+ napi_tx);
+ int tx_done = 0;
+ uint i;
+
+ /* always process ALL Tx complete, regardless budget - it is fast */
+ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
+ struct vring *vring = &wil->vring_tx[i];
+
+ if (!vring->va)
+ continue;
+
+ tx_done += wil_tx_complete(wil, i);
+ }
+
+ if (tx_done <= 1) { /* burst ends - only one packet processed */
+ napi_complete(napi);
+ wil6210_unmask_irq_tx(wil);
+ wil_dbg_txrx(wil, "NAPI TX complete\n");
+ }
+
+ wil_dbg_txrx(wil, "NAPI TX poll(%d) done %d\n", budget, tx_done);
+
+ return min(tx_done, budget);
+}
+
void *wil_if_alloc(struct device *dev, void __iomem *csr)
{
struct net_device *ndev;
@@ -81,6 +130,11 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
wdev->netdev = ndev;
+ netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx,
+ WIL6210_NAPI_BUDGET);
+ netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
+ WIL6210_NAPI_BUDGET);
+
wil_link_off(wil);
return wil;
diff --git a/drivers/net/wireless/ath/wil6210/trace.c b/drivers/net/wireless/ath/wil6210/trace.c
new file mode 100644
index 000000000000..cd2534b9c5aa
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/trace.c
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/drivers/net/wireless/ath/wil6210/trace.h b/drivers/net/wireless/ath/wil6210/trace.h
new file mode 100644
index 000000000000..eff1239be53a
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/trace.h
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM wil6210
+#if !defined(WIL6210_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define WIL6210_TRACE_H
+
+#include <linux/tracepoint.h>
+#include "wil6210.h"
+#include "txrx.h"
+
+/* create empty functions when tracing is disabled */
+#if !defined(CONFIG_WIL6210_TRACING) || defined(__CHECKER__)
+
+#undef TRACE_EVENT
+#define TRACE_EVENT(name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(...)
+#undef DEFINE_EVENT
+#define DEFINE_EVENT(evt_class, name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#endif /* !CONFIG_WIL6210_TRACING || defined(__CHECKER__) */
+
+DECLARE_EVENT_CLASS(wil6210_wmi,
+ TP_PROTO(u16 id, void *buf, u16 buf_len),
+
+ TP_ARGS(id, buf, buf_len),
+
+ TP_STRUCT__entry(
+ __field(u16, id)
+ __field(u16, buf_len)
+ __dynamic_array(u8, buf, buf_len)
+ ),
+
+ TP_fast_assign(
+ __entry->id = id;
+ __entry->buf_len = buf_len;
+ memcpy(__get_dynamic_array(buf), buf, buf_len);
+ ),
+
+ TP_printk(
+ "id 0x%04x len %d",
+ __entry->id, __entry->buf_len
+ )
+);
+
+DEFINE_EVENT(wil6210_wmi, wil6210_wmi_cmd,
+ TP_PROTO(u16 id, void *buf, u16 buf_len),
+ TP_ARGS(id, buf, buf_len)
+);
+
+DEFINE_EVENT(wil6210_wmi, wil6210_wmi_event,
+ TP_PROTO(u16 id, void *buf, u16 buf_len),
+ TP_ARGS(id, buf, buf_len)
+);
+
+#define WIL6210_MSG_MAX (200)
+
+DECLARE_EVENT_CLASS(wil6210_log_event,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf),
+ TP_STRUCT__entry(
+ __dynamic_array(char, msg, WIL6210_MSG_MAX)
+ ),
+ TP_fast_assign(
+ WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+ WIL6210_MSG_MAX,
+ vaf->fmt,
+ *vaf->va) >= WIL6210_MSG_MAX);
+ ),
+ TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(wil6210_log_event, wil6210_log_err,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(wil6210_log_event, wil6210_log_info,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(wil6210_log_event, wil6210_log_dbg,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+#define wil_pseudo_irq_cause(x) __print_flags(x, "|", \
+ {BIT_DMA_PSEUDO_CAUSE_RX, "Rx" }, \
+ {BIT_DMA_PSEUDO_CAUSE_TX, "Tx" }, \
+ {BIT_DMA_PSEUDO_CAUSE_MISC, "Misc" })
+
+TRACE_EVENT(wil6210_irq_pseudo,
+ TP_PROTO(u32 x),
+ TP_ARGS(x),
+ TP_STRUCT__entry(
+ __field(u32, x)
+ ),
+ TP_fast_assign(
+ __entry->x = x;
+ ),
+ TP_printk("cause 0x%08x : %s", __entry->x,
+ wil_pseudo_irq_cause(__entry->x))
+);
+
+DECLARE_EVENT_CLASS(wil6210_irq,
+ TP_PROTO(u32 x),
+ TP_ARGS(x),
+ TP_STRUCT__entry(
+ __field(u32, x)
+ ),
+ TP_fast_assign(
+ __entry->x = x;
+ ),
+ TP_printk("cause 0x%08x", __entry->x)
+);
+
+DEFINE_EVENT(wil6210_irq, wil6210_irq_rx,
+ TP_PROTO(u32 x),
+ TP_ARGS(x)
+);
+
+DEFINE_EVENT(wil6210_irq, wil6210_irq_tx,
+ TP_PROTO(u32 x),
+ TP_ARGS(x)
+);
+
+DEFINE_EVENT(wil6210_irq, wil6210_irq_misc,
+ TP_PROTO(u32 x),
+ TP_ARGS(x)
+);
+
+DEFINE_EVENT(wil6210_irq, wil6210_irq_misc_thread,
+ TP_PROTO(u32 x),
+ TP_ARGS(x)
+);
+
+TRACE_EVENT(wil6210_rx,
+ TP_PROTO(u16 index, struct vring_rx_desc *d),
+ TP_ARGS(index, d),
+ TP_STRUCT__entry(
+ __field(u16, index)
+ __field(unsigned int, len)
+ __field(u8, mid)
+ __field(u8, cid)
+ __field(u8, tid)
+ __field(u8, type)
+ __field(u8, subtype)
+ __field(u16, seq)
+ __field(u8, mcs)
+ ),
+ TP_fast_assign(
+ __entry->index = index;
+ __entry->len = d->dma.length;
+ __entry->mid = wil_rxdesc_mid(d);
+ __entry->cid = wil_rxdesc_cid(d);
+ __entry->tid = wil_rxdesc_tid(d);
+ __entry->type = wil_rxdesc_ftype(d);
+ __entry->subtype = wil_rxdesc_subtype(d);
+ __entry->seq = wil_rxdesc_seq(d);
+ __entry->mcs = wil_rxdesc_mcs(d);
+ ),
+ TP_printk("index %d len %d mid %d cid %d tid %d mcs %d seq 0x%03x"
+ " type 0x%1x subtype 0x%1x", __entry->index, __entry->len,
+ __entry->mid, __entry->cid, __entry->tid, __entry->mcs,
+ __entry->seq, __entry->type, __entry->subtype)
+);
+
+TRACE_EVENT(wil6210_tx,
+ TP_PROTO(u8 vring, u16 index, unsigned int len, u8 frags),
+ TP_ARGS(vring, index, len, frags),
+ TP_STRUCT__entry(
+ __field(u8, vring)
+ __field(u8, frags)
+ __field(u16, index)
+ __field(unsigned int, len)
+ ),
+ TP_fast_assign(
+ __entry->vring = vring;
+ __entry->frags = frags;
+ __entry->index = index;
+ __entry->len = len;
+ ),
+ TP_printk("vring %d index %d len %d frags %d",
+ __entry->vring, __entry->index, __entry->len, __entry->frags)
+);
+
+TRACE_EVENT(wil6210_tx_done,
+ TP_PROTO(u8 vring, u16 index, unsigned int len, u8 err),
+ TP_ARGS(vring, index, len, err),
+ TP_STRUCT__entry(
+ __field(u8, vring)
+ __field(u8, err)
+ __field(u16, index)
+ __field(unsigned int, len)
+ ),
+ TP_fast_assign(
+ __entry->vring = vring;
+ __entry->index = index;
+ __entry->len = len;
+ __entry->err = err;
+ ),
+ TP_printk("vring %d index %d len %d err 0x%02x",
+ __entry->vring, __entry->index, __entry->len,
+ __entry->err)
+);
+
+#endif /* WIL6210_TRACE_H || TRACE_HEADER_MULTI_READ*/
+
+#if defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__)
+/* we don't want to use include/trace/events */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
+#endif /* defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__) */
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 797024507c71..00dffeda983e 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -22,6 +22,7 @@
#include "wil6210.h"
#include "wmi.h"
#include "txrx.h"
+#include "trace.h"
static bool rtap_include_phy_info;
module_param(rtap_include_phy_info, bool, S_IRUGO);
@@ -89,8 +90,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
* we can use any
*/
for (i = 0; i < vring->size; i++) {
- volatile struct vring_tx_desc *d = &(vring->va[i].tx);
- d->dma.status = TX_DMA_STATUS_DU;
+ volatile struct vring_tx_desc *_d = &(vring->va[i].tx);
+ _d->dma.status = TX_DMA_STATUS_DU;
}
wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size,
@@ -106,30 +107,39 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
size_t sz = vring->size * sizeof(vring->va[0]);
while (!wil_vring_is_empty(vring)) {
+ dma_addr_t pa;
+ struct sk_buff *skb;
+ u16 dmalen;
+
if (tx) {
- volatile struct vring_tx_desc *d =
+ struct vring_tx_desc dd, *d = &dd;
+ volatile struct vring_tx_desc *_d =
&vring->va[vring->swtail].tx;
- dma_addr_t pa = d->dma.addr_low |
- ((u64)d->dma.addr_high << 32);
- struct sk_buff *skb = vring->ctx[vring->swtail];
+
+ *d = *_d;
+ pa = wil_desc_addr(&d->dma.addr);
+ dmalen = le16_to_cpu(d->dma.length);
+ skb = vring->ctx[vring->swtail];
if (skb) {
- dma_unmap_single(dev, pa, d->dma.length,
+ dma_unmap_single(dev, pa, dmalen,
DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
vring->ctx[vring->swtail] = NULL;
} else {
- dma_unmap_page(dev, pa, d->dma.length,
+ dma_unmap_page(dev, pa, dmalen,
DMA_TO_DEVICE);
}
vring->swtail = wil_vring_next_tail(vring);
} else { /* rx */
- volatile struct vring_rx_desc *d =
+ struct vring_rx_desc dd, *d = &dd;
+ volatile struct vring_rx_desc *_d =
&vring->va[vring->swtail].rx;
- dma_addr_t pa = d->dma.addr_low |
- ((u64)d->dma.addr_high << 32);
- struct sk_buff *skb = vring->ctx[vring->swhead];
- dma_unmap_single(dev, pa, d->dma.length,
- DMA_FROM_DEVICE);
+
+ *d = *_d;
+ pa = wil_desc_addr(&d->dma.addr);
+ dmalen = le16_to_cpu(d->dma.length);
+ skb = vring->ctx[vring->swhead];
+ dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE);
kfree_skb(skb);
wil_vring_advance_head(vring, 1);
}
@@ -151,7 +161,8 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
{
struct device *dev = wil_to_dev(wil);
unsigned int sz = RX_BUF_LEN;
- volatile struct vring_rx_desc *d = &(vring->va[i].rx);
+ struct vring_rx_desc dd, *d = &dd;
+ volatile struct vring_rx_desc *_d = &(vring->va[i].rx);
dma_addr_t pa;
/* TODO align */
@@ -169,13 +180,13 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
}
d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT;
- d->dma.addr_low = lower_32_bits(pa);
- d->dma.addr_high = (u16)upper_32_bits(pa);
+ wil_desc_addr_set(&d->dma.addr, pa);
/* ip_length don't care */
/* b11 don't care */
/* error don't care */
d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
- d->dma.length = sz;
+ d->dma.length = cpu_to_le16(sz);
+ *_d = *d;
vring->ctx[i] = skb;
return 0;
@@ -321,11 +332,12 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
{
struct device *dev = wil_to_dev(wil);
struct net_device *ndev = wil_to_ndev(wil);
- volatile struct vring_rx_desc *d;
- struct vring_rx_desc *d1;
+ volatile struct vring_rx_desc *_d;
+ struct vring_rx_desc *d;
struct sk_buff *skb;
dma_addr_t pa;
unsigned int sz = RX_BUF_LEN;
+ u16 dmalen;
u8 ftype;
u8 ds_bits;
@@ -334,32 +346,44 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
if (wil_vring_is_empty(vring))
return NULL;
- d = &(vring->va[vring->swhead].rx);
- if (!(d->dma.status & RX_DMA_STATUS_DU)) {
+ _d = &(vring->va[vring->swhead].rx);
+ if (!(_d->dma.status & RX_DMA_STATUS_DU)) {
/* it is not error, we just reached end of Rx done area */
return NULL;
}
- pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
skb = vring->ctx[vring->swhead];
+ d = wil_skb_rxdesc(skb);
+ *d = *_d;
+ pa = wil_desc_addr(&d->dma.addr);
+ vring->ctx[vring->swhead] = NULL;
+ wil_vring_advance_head(vring, 1);
+
dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE);
- skb_trim(skb, d->dma.length);
+ dmalen = le16_to_cpu(d->dma.length);
- d1 = wil_skb_rxdesc(skb);
- *d1 = *d;
+ trace_wil6210_rx(vring->swhead, d);
+ wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, dmalen);
+ wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4,
+ (const void *)d, sizeof(*d), false);
- wil->stats.last_mcs_rx = wil_rxdesc_mcs(d1);
+ if (dmalen > sz) {
+ wil_err(wil, "Rx size too large: %d bytes!\n", dmalen);
+ kfree_skb(skb);
+ return NULL;
+ }
+ skb_trim(skb, dmalen);
+
+ wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
+ skb->data, skb_headlen(skb), false);
+
+
+ wil->stats.last_mcs_rx = wil_rxdesc_mcs(d);
/* use radiotap header only if required */
if (ndev->type == ARPHRD_IEEE80211_RADIOTAP)
wil_rx_add_radiotap_header(wil, skb);
- wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length);
- wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4,
- (const void *)d, sizeof(*d), false);
-
- wil_vring_advance_head(vring, 1);
-
/* no extra checks if in sniffer mode */
if (ndev->type != ARPHRD_ETHER)
return skb;
@@ -368,7 +392,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
* Driver should recognize it by frame type, that is found
* in Rx descriptor. If type is not data, it is 802.11 frame as is
*/
- ftype = wil_rxdesc_ftype(d1) << 2;
+ ftype = wil_rxdesc_ftype(d) << 2;
if (ftype != IEEE80211_FTYPE_DATA) {
wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype);
/* TODO: process it */
@@ -383,7 +407,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
return NULL;
}
- ds_bits = wil_rxdesc_ds_bits(d1);
+ ds_bits = wil_rxdesc_ds_bits(d);
if (ds_bits == 1) {
/*
* HW bug - in ToDS mode, i.e. Rx on AP side,
@@ -425,6 +449,7 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count)
/*
* Pass Rx packet to the netif. Update statistics.
+ * Called in softirq context (NAPI poll).
*/
static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
{
@@ -433,10 +458,7 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
skb_orphan(skb);
- if (in_interrupt())
- rc = netif_rx(skb);
- else
- rc = netif_rx_ni(skb);
+ rc = netif_receive_skb(skb);
if (likely(rc == NET_RX_SUCCESS)) {
ndev->stats.rx_packets++;
@@ -450,9 +472,9 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
/**
* Proceed all completed skb's from Rx VRING
*
- * Safe to call from IRQ
+ * Safe to call from NAPI poll, i.e. softirq with interrupts enabled
*/
-void wil_rx_handle(struct wil6210_priv *wil)
+void wil_rx_handle(struct wil6210_priv *wil, int *quota)
{
struct net_device *ndev = wil_to_ndev(wil);
struct vring *v = &wil->vring_rx;
@@ -463,9 +485,8 @@ void wil_rx_handle(struct wil6210_priv *wil)
return;
}
wil_dbg_txrx(wil, "%s()\n", __func__);
- while (NULL != (skb = wil_vring_reap_rx(wil, v))) {
- wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
- skb->data, skb_headlen(skb), false);
+ while ((*quota > 0) && (NULL != (skb = wil_vring_reap_rx(wil, v)))) {
+ (*quota)--;
if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
skb->dev = ndev;
@@ -600,17 +621,15 @@ static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,
return NULL;
}
-static int wil_tx_desc_map(volatile struct vring_tx_desc *d,
- dma_addr_t pa, u32 len)
+static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len)
{
- d->dma.addr_low = lower_32_bits(pa);
- d->dma.addr_high = (u16)upper_32_bits(pa);
+ wil_desc_addr_set(&d->dma.addr, pa);
d->dma.ip_length = 0;
/* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/
d->dma.b11 = 0/*14 | BIT(7)*/;
d->dma.error = 0;
d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
- d->dma.length = len;
+ d->dma.length = cpu_to_le16((u16)len);
d->dma.d0 = 0;
d->mac.d[0] = 0;
d->mac.d[1] = 0;
@@ -630,7 +649,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
struct sk_buff *skb)
{
struct device *dev = wil_to_dev(wil);
- volatile struct vring_tx_desc *d;
+ struct vring_tx_desc dd, *d = &dd;
+ volatile struct vring_tx_desc *_d;
u32 swhead = vring->swhead;
int avail = wil_vring_avail_tx(vring);
int nr_frags = skb_shinfo(skb)->nr_frags;
@@ -648,7 +668,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
1 + nr_frags);
return -ENOMEM;
}
- d = &(vring->va[i].tx);
+ _d = &(vring->va[i].tx);
/* FIXME FW can accept only unicast frames for the peer */
memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN);
@@ -667,25 +687,30 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
wil_tx_desc_map(d, pa, skb_headlen(skb));
d->mac.d[2] |= ((nr_frags + 1) <<
MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS);
+ if (nr_frags)
+ *_d = *d;
+
/* middle segments */
for (f = 0; f < nr_frags; f++) {
const struct skb_frag_struct *frag =
&skb_shinfo(skb)->frags[f];
int len = skb_frag_size(frag);
i = (swhead + f + 1) % vring->size;
- d = &(vring->va[i].tx);
+ _d = &(vring->va[i].tx);
pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev, pa)))
goto dma_error;
wil_tx_desc_map(d, pa, len);
vring->ctx[i] = NULL;
+ *_d = *d;
}
/* for the last seg only */
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS);
d->dma.d0 |= BIT(9); /* BUG: undocumented bit */
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS);
+ *_d = *d;
wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
@@ -693,6 +718,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
/* advance swhead */
wil_vring_advance_head(vring, nr_frags + 1);
wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
+ trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags);
iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
/* hold reference to skb
* to prevent skb release before accounting
@@ -705,14 +731,18 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
/* unmap what we have mapped */
/* Note: increment @f to operate with positive index */
for (f++; f > 0; f--) {
+ u16 dmalen;
+
i = (swhead + f) % vring->size;
- d = &(vring->va[i].tx);
- d->dma.status = TX_DMA_STATUS_DU;
- pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
+ _d = &(vring->va[i].tx);
+ *d = *_d;
+ _d->dma.status = TX_DMA_STATUS_DU;
+ pa = wil_desc_addr(&d->dma.addr);
+ dmalen = le16_to_cpu(d->dma.length);
if (vring->ctx[i])
- dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE);
+ dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
else
- dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE);
+ dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
}
return -EINVAL;
@@ -761,7 +791,6 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
break; /* goto drop; */
}
drop:
- netif_tx_stop_all_queues(ndev);
ndev->stats.tx_dropped++;
dev_kfree_skb_any(skb);
@@ -771,41 +800,48 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
/**
* Clean up transmitted skb's from the Tx VRING
*
+ * Return number of descriptors cleared
+ *
* Safe to call from IRQ
*/
-void wil_tx_complete(struct wil6210_priv *wil, int ringid)
+int wil_tx_complete(struct wil6210_priv *wil, int ringid)
{
struct net_device *ndev = wil_to_ndev(wil);
struct device *dev = wil_to_dev(wil);
struct vring *vring = &wil->vring_tx[ringid];
+ int done = 0;
if (!vring->va) {
wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid);
- return;
+ return 0;
}
wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid);
while (!wil_vring_is_empty(vring)) {
- volatile struct vring_tx_desc *d1 =
+ volatile struct vring_tx_desc *_d =
&vring->va[vring->swtail].tx;
struct vring_tx_desc dd, *d = &dd;
dma_addr_t pa;
struct sk_buff *skb;
+ u16 dmalen;
- dd = *d1;
+ *d = *_d;
if (!(d->dma.status & TX_DMA_STATUS_DU))
break;
+ dmalen = le16_to_cpu(d->dma.length);
+ trace_wil6210_tx_done(ringid, vring->swtail, dmalen,
+ d->dma.error);
wil_dbg_txrx(wil,
"Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
- vring->swtail, d->dma.length, d->dma.status,
+ vring->swtail, dmalen, d->dma.status,
d->dma.error);
wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
- pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
+ pa = wil_desc_addr(&d->dma.addr);
skb = vring->ctx[vring->swtail];
if (skb) {
if (d->dma.error == 0) {
@@ -815,18 +851,21 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid)
ndev->stats.tx_errors++;
}
- dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE);
+ dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
vring->ctx[vring->swtail] = NULL;
} else {
- dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE);
+ dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
}
- d->dma.addr_low = 0;
- d->dma.addr_high = 0;
+ d->dma.addr.addr_low = 0;
+ d->dma.addr.addr_high = 0;
d->dma.length = 0;
d->dma.status = TX_DMA_STATUS_DU;
vring->swtail = wil_vring_next_tail(vring);
+ done++;
}
if (wil_vring_avail_tx(vring) > vring->size/4)
netif_tx_wake_all_queues(wil_to_ndev(wil));
+
+ return done;
}
diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h
index adef12fb2aee..23c0781cdb93 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.h
+++ b/drivers/net/wireless/ath/wil6210/txrx.h
@@ -27,6 +27,28 @@
#define WIL6210_RTAP_SIZE (128)
/* Tx/Rx path */
+
+/*
+ * Common representation of physical address in Vring
+ */
+struct vring_dma_addr {
+ __le32 addr_low;
+ __le16 addr_high;
+} __packed;
+
+static inline dma_addr_t wil_desc_addr(struct vring_dma_addr *addr)
+{
+ return le32_to_cpu(addr->addr_low) |
+ ((u64)le16_to_cpu(addr->addr_high) << 32);
+}
+
+static inline void wil_desc_addr_set(struct vring_dma_addr *addr,
+ dma_addr_t pa)
+{
+ addr->addr_low = cpu_to_le32(lower_32_bits(pa));
+ addr->addr_high = cpu_to_le16((u16)upper_32_bits(pa));
+}
+
/*
* Tx descriptor - MAC part
* [dword 0]
@@ -216,13 +238,12 @@ struct vring_tx_mac {
struct vring_tx_dma {
u32 d0;
- u32 addr_low;
- u16 addr_high;
+ struct vring_dma_addr addr;
u8 ip_length;
u8 b11; /* 0..6: mac_length; 7:ip_version */
u8 error; /* 0..2: err; 3..7: reserved; */
u8 status; /* 0: used; 1..7; reserved */
- u16 length;
+ __le16 length;
} __packed;
/*
@@ -315,13 +336,12 @@ struct vring_rx_mac {
struct vring_rx_dma {
u32 d0;
- u32 addr_low;
- u16 addr_high;
+ struct vring_dma_addr addr;
u8 ip_length;
u8 b11;
u8 error;
u8 status;
- u16 length;
+ __le16 length;
} __packed;
struct vring_tx_desc {
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 8f76ecd8a7e5..373cf656f5b0 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -34,9 +34,11 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
#define WIL6210_MEM_SIZE (2*1024*1024UL)
-#define WIL6210_RX_RING_SIZE (128)
-#define WIL6210_TX_RING_SIZE (128)
-#define WIL6210_MAX_TX_RINGS (24)
+#define WIL6210_RX_RING_SIZE (128)
+#define WIL6210_TX_RING_SIZE (128)
+#define WIL6210_MAX_TX_RINGS (24) /* HW limit */
+#define WIL6210_MAX_CID (8) /* HW limit */
+#define WIL6210_NAPI_BUDGET (16) /* arbitrary */
/* Hardware definitions begin */
@@ -184,6 +186,7 @@ struct vring {
enum { /* for wil6210_priv.status */
wil_status_fwready = 0,
+ wil_status_fwconnecting,
wil_status_fwconnected,
wil_status_dontscan,
wil_status_reset_done,
@@ -239,6 +242,8 @@ struct wil6210_priv {
* - consumed in thread by wmi_event_worker
*/
spinlock_t wmi_ev_lock;
+ struct napi_struct napi_rx;
+ struct napi_struct napi_tx;
/* DMA related */
struct vring vring_rx;
struct vring vring_tx[WIL6210_MAX_TX_RINGS];
@@ -267,9 +272,13 @@ struct wil6210_priv {
#define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr))
-#define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg)
-#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg)
-#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg)
+int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...);
+int wil_err(struct wil6210_priv *wil, const char *fmt, ...);
+int wil_info(struct wil6210_priv *wil, const char *fmt, ...);
+#define wil_dbg(wil, fmt, arg...) do { \
+ netdev_dbg(wil_to_ndev(wil), fmt, ##arg); \
+ wil_dbg_trace(wil, fmt, ##arg); \
+} while (0)
#define wil_dbg_irq(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg)
#define wil_dbg_txrx(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg)
@@ -356,10 +365,12 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
void wil_vring_fini_tx(struct wil6210_priv *wil, int id);
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev);
-void wil_tx_complete(struct wil6210_priv *wil, int ringid);
+int wil_tx_complete(struct wil6210_priv *wil, int ringid);
+void wil6210_unmask_irq_tx(struct wil6210_priv *wil);
/* RX API */
-void wil_rx_handle(struct wil6210_priv *wil);
+void wil_rx_handle(struct wil6210_priv *wil, int *quota);
+void wil6210_unmask_irq_rx(struct wil6210_priv *wil);
int wil_iftype_nl2wmi(enum nl80211_iftype type);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 45b04e383f9a..527ffb543821 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -20,6 +20,7 @@
#include "wil6210.h"
#include "txrx.h"
#include "wmi.h"
+#include "trace.h"
/**
* WMI event receiving - theory of operations
@@ -246,6 +247,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
iowrite32(r->head = next_head, wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, tx.head));
+ trace_wil6210_wmi_cmd(cmdid, buf, len);
+
/* interrupt to FW */
iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT);
@@ -406,7 +409,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
(wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
- if (wdev->sme_state != CFG80211_SME_CONNECTING) {
+ if (!test_bit(wil_status_fwconnecting, &wil->status)) {
wil_err(wil, "Not in connecting state\n");
return;
}
@@ -430,6 +433,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL);
}
+ clear_bit(wil_status_fwconnecting, &wil->status);
set_bit(wil_status_fwconnected, &wil->status);
/* FIXME FW can transmit only ucast frames to peer */
@@ -635,8 +639,9 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
hdr.flags);
if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
(len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
- wil_dbg_wmi(wil, "WMI event 0x%04x\n",
- evt->event.wmi.id);
+ u16 id = le16_to_cpu(evt->event.wmi.id);
+ wil_dbg_wmi(wil, "WMI event 0x%04x\n", id);
+ trace_wil6210_wmi_event(id, &evt->event.wmi, len);
}
wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1,
&evt->event.hdr, sizeof(hdr) + len, true);
@@ -724,7 +729,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)
.bcon_interval = cpu_to_le16(bi),
.network_type = wmi_nettype,
.disable_sec_offload = 1,
- .channel = chan,
+ .channel = chan - 1,
};
struct {
struct wil6210_mbox_hdr_wmi wmi;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
index 44fa0cdbf97b..11400b39cf0b 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
@@ -606,7 +606,8 @@ static int brcmf_sdio_pd_remove(struct platform_device *pdev)
static struct platform_driver brcmf_sdio_pd = {
.remove = brcmf_sdio_pd_remove,
.driver = {
- .name = BRCMFMAC_SDIO_PDATA_NAME
+ .name = BRCMFMAC_SDIO_PDATA_NAME,
+ .owner = THIS_MODULE,
}
};
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
index 1585cc5bf866..bd982856d385 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
@@ -900,7 +900,7 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
if (supr_status) {
update_rate = false;
if (supr_status == TX_STATUS_SUPR_BADCH) {
- brcms_err(wlc->hw->d11core,
+ brcms_dbg_ht(wlc->hw->d11core,
"%s: Pkt tx suppressed, illegal channel possibly %d\n",
__func__, CHSPEC_CHANNEL(
wlc->default_bss->chanspec));
diff --git a/drivers/net/wireless/cw1200/Kconfig b/drivers/net/wireless/cw1200/Kconfig
new file mode 100644
index 000000000000..0880742eab17
--- /dev/null
+++ b/drivers/net/wireless/cw1200/Kconfig
@@ -0,0 +1,30 @@
+config CW1200
+ tristate "CW1200 WLAN support"
+ depends on MAC80211 && CFG80211
+ help
+ This is a driver for the ST-E CW1100 & CW1200 WLAN chipsets.
+ This option just enables the driver core, see below for
+ specific bus support.
+
+if CW1200
+
+config CW1200_WLAN_SDIO
+ tristate "Support SDIO platforms"
+ depends on CW1200 && MMC
+ help
+ Enable support for the CW1200 connected via an SDIO bus.
+ By default this driver only supports the Sagrad SG901-1091/1098 EVK
+ and similar designs that utilize a hardware reset circuit. To
+ support different CW1200 SDIO designs you will need to override
+ the default platform data by calling cw1200_sdio_set_platform_data()
+ in your board setup file.
+
+config CW1200_WLAN_SPI
+ tristate "Support SPI platforms"
+ depends on CW1200 && SPI
+ help
+ Enables support for the CW1200 connected via a SPI bus. You will
+ need to add appropriate platform data glue in your board setup
+ file.
+
+endif
diff --git a/drivers/net/wireless/cw1200/Makefile b/drivers/net/wireless/cw1200/Makefile
new file mode 100644
index 000000000000..b086aac6547a
--- /dev/null
+++ b/drivers/net/wireless/cw1200/Makefile
@@ -0,0 +1,21 @@
+cw1200_core-y := \
+ fwio.o \
+ txrx.o \
+ main.o \
+ queue.o \
+ hwio.o \
+ bh.o \
+ wsm.o \
+ sta.o \
+ scan.o \
+ debug.o
+cw1200_core-$(CONFIG_PM) += pm.o
+
+# CFLAGS_sta.o += -DDEBUG
+
+cw1200_wlan_sdio-y := cw1200_sdio.o
+cw1200_wlan_spi-y := cw1200_spi.o
+
+obj-$(CONFIG_CW1200) += cw1200_core.o
+obj-$(CONFIG_CW1200_WLAN_SDIO) += cw1200_wlan_sdio.o
+obj-$(CONFIG_CW1200_WLAN_SPI) += cw1200_wlan_spi.o
diff --git a/drivers/net/wireless/cw1200/bh.c b/drivers/net/wireless/cw1200/bh.c
new file mode 100644
index 000000000000..c1ec2a4dd8c0
--- /dev/null
+++ b/drivers/net/wireless/cw1200/bh.c
@@ -0,0 +1,619 @@
+/*
+ * Device handling thread implementation for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver, which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@stericsson.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 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <linux/kthread.h>
+#include <linux/timer.h>
+
+#include "cw1200.h"
+#include "bh.h"
+#include "hwio.h"
+#include "wsm.h"
+#include "hwbus.h"
+#include "debug.h"
+#include "fwio.h"
+
+static int cw1200_bh(void *arg);
+
+#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4)
+/* an SPI message cannot be bigger than (2"12-1)*2 bytes
+ * "*2" to cvt to bytes
+ */
+#define MAX_SZ_RD_WR_BUFFERS (DOWNLOAD_BLOCK_SIZE_WR*2)
+#define PIGGYBACK_CTRL_REG (2)
+#define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG)
+
+/* Suspend state privates */
+enum cw1200_bh_pm_state {
+ CW1200_BH_RESUMED = 0,
+ CW1200_BH_SUSPEND,
+ CW1200_BH_SUSPENDED,
+ CW1200_BH_RESUME,
+};
+
+typedef int (*cw1200_wsm_handler)(struct cw1200_common *priv,
+ u8 *data, size_t size);
+
+static void cw1200_bh_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, bh_work);
+ cw1200_bh(priv);
+}
+
+int cw1200_register_bh(struct cw1200_common *priv)
+{
+ int err = 0;
+ /* Realtime workqueue */
+ priv->bh_workqueue = alloc_workqueue("cw1200_bh",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI
+ | WQ_CPU_INTENSIVE, 1);
+
+ if (!priv->bh_workqueue)
+ return -ENOMEM;
+
+ INIT_WORK(&priv->bh_work, cw1200_bh_work);
+
+ pr_debug("[BH] register.\n");
+
+ atomic_set(&priv->bh_rx, 0);
+ atomic_set(&priv->bh_tx, 0);
+ atomic_set(&priv->bh_term, 0);
+ atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
+ priv->bh_error = 0;
+ priv->hw_bufs_used = 0;
+ priv->buf_id_tx = 0;
+ priv->buf_id_rx = 0;
+ init_waitqueue_head(&priv->bh_wq);
+ init_waitqueue_head(&priv->bh_evt_wq);
+
+ err = !queue_work(priv->bh_workqueue, &priv->bh_work);
+ WARN_ON(err);
+ return err;
+}
+
+void cw1200_unregister_bh(struct cw1200_common *priv)
+{
+ atomic_add(1, &priv->bh_term);
+ wake_up(&priv->bh_wq);
+
+ flush_workqueue(priv->bh_workqueue);
+
+ destroy_workqueue(priv->bh_workqueue);
+ priv->bh_workqueue = NULL;
+
+ pr_debug("[BH] unregistered.\n");
+}
+
+void cw1200_irq_handler(struct cw1200_common *priv)
+{
+ pr_debug("[BH] irq.\n");
+
+ /* Disable Interrupts! */
+ /* NOTE: hwbus_ops->lock already held */
+ __cw1200_irq_enable(priv, 0);
+
+ if (/* WARN_ON */(priv->bh_error))
+ return;
+
+ if (atomic_add_return(1, &priv->bh_rx) == 1)
+ wake_up(&priv->bh_wq);
+}
+EXPORT_SYMBOL_GPL(cw1200_irq_handler);
+
+void cw1200_bh_wakeup(struct cw1200_common *priv)
+{
+ pr_debug("[BH] wakeup.\n");
+ if (priv->bh_error) {
+ pr_err("[BH] wakeup failed (BH error)\n");
+ return;
+ }
+
+ if (atomic_add_return(1, &priv->bh_tx) == 1)
+ wake_up(&priv->bh_wq);
+}
+
+int cw1200_bh_suspend(struct cw1200_common *priv)
+{
+ pr_debug("[BH] suspend.\n");
+ if (priv->bh_error) {
+ wiphy_warn(priv->hw->wiphy, "BH error -- can't suspend\n");
+ return -EINVAL;
+ }
+
+ atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND);
+ wake_up(&priv->bh_wq);
+ return wait_event_timeout(priv->bh_evt_wq, priv->bh_error ||
+ (CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend)),
+ 1 * HZ) ? 0 : -ETIMEDOUT;
+}
+
+int cw1200_bh_resume(struct cw1200_common *priv)
+{
+ pr_debug("[BH] resume.\n");
+ if (priv->bh_error) {
+ wiphy_warn(priv->hw->wiphy, "BH error -- can't resume\n");
+ return -EINVAL;
+ }
+
+ atomic_set(&priv->bh_suspend, CW1200_BH_RESUME);
+ wake_up(&priv->bh_wq);
+ return wait_event_timeout(priv->bh_evt_wq, priv->bh_error ||
+ (CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend)),
+ 1 * HZ) ? 0 : -ETIMEDOUT;
+}
+
+static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv)
+{
+ ++priv->hw_bufs_used;
+}
+
+int wsm_release_tx_buffer(struct cw1200_common *priv, int count)
+{
+ int ret = 0;
+ int hw_bufs_used = priv->hw_bufs_used;
+
+ priv->hw_bufs_used -= count;
+ if (WARN_ON(priv->hw_bufs_used < 0))
+ ret = -1;
+ else if (hw_bufs_used >= priv->wsm_caps.input_buffers)
+ ret = 1;
+ if (!priv->hw_bufs_used)
+ wake_up(&priv->bh_evt_wq);
+ return ret;
+}
+
+static int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv,
+ u16 *ctrl_reg)
+{
+ int ret;
+
+ ret = cw1200_reg_read_16(priv,
+ ST90TDS_CONTROL_REG_ID, ctrl_reg);
+ if (ret) {
+ ret = cw1200_reg_read_16(priv,
+ ST90TDS_CONTROL_REG_ID, ctrl_reg);
+ if (ret)
+ pr_err("[BH] Failed to read control register.\n");
+ }
+
+ return ret;
+}
+
+static int cw1200_device_wakeup(struct cw1200_common *priv)
+{
+ u16 ctrl_reg;
+ int ret;
+
+ pr_debug("[BH] Device wakeup.\n");
+
+ /* First, set the dpll register */
+ ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID,
+ cw1200_dpll_from_clk(priv->hw_refclk));
+ if (WARN_ON(ret))
+ return ret;
+
+ /* To force the device to be always-on, the host sets WLAN_UP to 1 */
+ ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID,
+ ST90TDS_CONT_WUP_BIT);
+ if (WARN_ON(ret))
+ return ret;
+
+ ret = cw1200_bh_read_ctrl_reg(priv, &ctrl_reg);
+ if (WARN_ON(ret))
+ return ret;
+
+ /* If the device returns WLAN_RDY as 1, the device is active and will
+ * remain active.
+ */
+ if (ctrl_reg & ST90TDS_CONT_RDY_BIT) {
+ pr_debug("[BH] Device awake.\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Must be called from BH thraed. */
+void cw1200_enable_powersave(struct cw1200_common *priv,
+ bool enable)
+{
+ pr_debug("[BH] Powerave is %s.\n",
+ enable ? "enabled" : "disabled");
+ priv->powersave_enabled = enable;
+}
+
+static int cw1200_bh_rx_helper(struct cw1200_common *priv,
+ uint16_t *ctrl_reg,
+ int *tx)
+{
+ size_t read_len = 0;
+ struct sk_buff *skb_rx = NULL;
+ struct wsm_hdr *wsm;
+ size_t wsm_len;
+ u16 wsm_id;
+ u8 wsm_seq;
+ int rx_resync = 1;
+
+ size_t alloc_len;
+ u8 *data;
+
+ read_len = (*ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2;
+ if (!read_len)
+ return 0; /* No more work */
+
+ if (WARN_ON((read_len < sizeof(struct wsm_hdr)) ||
+ (read_len > EFFECTIVE_BUF_SIZE))) {
+ pr_debug("Invalid read len: %zu (%04x)",
+ read_len, *ctrl_reg);
+ goto err;
+ }
+
+ /* Add SIZE of PIGGYBACK reg (CONTROL Reg)
+ * to the NEXT Message length + 2 Bytes for SKB
+ */
+ read_len = read_len + 2;
+
+ alloc_len = priv->hwbus_ops->align_size(
+ priv->hwbus_priv, read_len);
+
+ /* Check if not exceeding CW1200 capabilities */
+ if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) {
+ pr_debug("Read aligned len: %zu\n",
+ alloc_len);
+ }
+
+ skb_rx = dev_alloc_skb(alloc_len);
+ if (WARN_ON(!skb_rx))
+ goto err;
+
+ skb_trim(skb_rx, 0);
+ skb_put(skb_rx, read_len);
+ data = skb_rx->data;
+ if (WARN_ON(!data))
+ goto err;
+
+ if (WARN_ON(cw1200_data_read(priv, data, alloc_len))) {
+ pr_err("rx blew up, len %zu\n", alloc_len);
+ goto err;
+ }
+
+ /* Piggyback */
+ *ctrl_reg = __le16_to_cpu(
+ ((__le16 *)data)[alloc_len / 2 - 1]);
+
+ wsm = (struct wsm_hdr *)data;
+ wsm_len = __le16_to_cpu(wsm->len);
+ if (WARN_ON(wsm_len > read_len))
+ goto err;
+
+ if (priv->wsm_enable_wsm_dumps)
+ print_hex_dump_bytes("<-- ",
+ DUMP_PREFIX_NONE,
+ data, wsm_len);
+
+ wsm_id = __le16_to_cpu(wsm->id) & 0xFFF;
+ wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7;
+
+ skb_trim(skb_rx, wsm_len);
+
+ if (wsm_id == 0x0800) {
+ wsm_handle_exception(priv,
+ &data[sizeof(*wsm)],
+ wsm_len - sizeof(*wsm));
+ goto err;
+ } else if (!rx_resync) {
+ if (WARN_ON(wsm_seq != priv->wsm_rx_seq))
+ goto err;
+ }
+ priv->wsm_rx_seq = (wsm_seq + 1) & 7;
+ rx_resync = 0;
+
+ if (wsm_id & 0x0400) {
+ int rc = wsm_release_tx_buffer(priv, 1);
+ if (WARN_ON(rc < 0))
+ return rc;
+ else if (rc > 0)
+ *tx = 1;
+ }
+
+ /* cw1200_wsm_rx takes care on SKB livetime */
+ if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb_rx)))
+ goto err;
+
+ if (skb_rx) {
+ dev_kfree_skb(skb_rx);
+ skb_rx = NULL;
+ }
+
+ return 0;
+
+err:
+ if (skb_rx) {
+ dev_kfree_skb(skb_rx);
+ skb_rx = NULL;
+ }
+ return -1;
+}
+
+static int cw1200_bh_tx_helper(struct cw1200_common *priv,
+ int *pending_tx,
+ int *tx_burst)
+{
+ size_t tx_len;
+ u8 *data;
+ int ret;
+ struct wsm_hdr *wsm;
+
+ if (priv->device_can_sleep) {
+ ret = cw1200_device_wakeup(priv);
+ if (WARN_ON(ret < 0)) { /* Error in wakeup */
+ *pending_tx = 1;
+ return 0;
+ } else if (ret) { /* Woke up */
+ priv->device_can_sleep = false;
+ } else { /* Did not awake */
+ *pending_tx = 1;
+ return 0;
+ }
+ }
+
+ wsm_alloc_tx_buffer(priv);
+ ret = wsm_get_tx(priv, &data, &tx_len, tx_burst);
+ if (ret <= 0) {
+ wsm_release_tx_buffer(priv, 1);
+ if (WARN_ON(ret < 0))
+ return ret; /* Error */
+ return 0; /* No work */
+ }
+
+ wsm = (struct wsm_hdr *)data;
+ BUG_ON(tx_len < sizeof(*wsm));
+ BUG_ON(__le16_to_cpu(wsm->len) != tx_len);
+
+ atomic_add(1, &priv->bh_tx);
+
+ tx_len = priv->hwbus_ops->align_size(
+ priv->hwbus_priv, tx_len);
+
+ /* Check if not exceeding CW1200 capabilities */
+ if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE))
+ pr_debug("Write aligned len: %zu\n", tx_len);
+
+ wsm->id &= __cpu_to_le16(0xffff ^ WSM_TX_SEQ(WSM_TX_SEQ_MAX));
+ wsm->id |= __cpu_to_le16(WSM_TX_SEQ(priv->wsm_tx_seq));
+
+ if (WARN_ON(cw1200_data_write(priv, data, tx_len))) {
+ pr_err("tx blew up, len %zu\n", tx_len);
+ wsm_release_tx_buffer(priv, 1);
+ return -1; /* Error */
+ }
+
+ if (priv->wsm_enable_wsm_dumps)
+ print_hex_dump_bytes("--> ",
+ DUMP_PREFIX_NONE,
+ data,
+ __le16_to_cpu(wsm->len));
+
+ wsm_txed(priv, data);
+ priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX;
+
+ if (*tx_burst > 1) {
+ cw1200_debug_tx_burst(priv);
+ return 1; /* Work remains */
+ }
+
+ return 0;
+}
+
+static int cw1200_bh(void *arg)
+{
+ struct cw1200_common *priv = arg;
+ int rx, tx, term, suspend;
+ u16 ctrl_reg = 0;
+ int tx_allowed;
+ int pending_tx = 0;
+ int tx_burst;
+ long status;
+ u32 dummy;
+ int ret;
+
+ for (;;) {
+ if (!priv->hw_bufs_used &&
+ priv->powersave_enabled &&
+ !priv->device_can_sleep &&
+ !atomic_read(&priv->recent_scan)) {
+ status = 1 * HZ;
+ pr_debug("[BH] Device wakedown. No data.\n");
+ cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0);
+ priv->device_can_sleep = true;
+ } else if (priv->hw_bufs_used) {
+ /* Interrupt loss detection */
+ status = 1 * HZ;
+ } else {
+ status = MAX_SCHEDULE_TIMEOUT;
+ }
+
+ /* Dummy Read for SDIO retry mechanism*/
+ if ((priv->hw_type != -1) &&
+ (atomic_read(&priv->bh_rx) == 0) &&
+ (atomic_read(&priv->bh_tx) == 0))
+ cw1200_reg_read(priv, ST90TDS_CONFIG_REG_ID,
+ &dummy, sizeof(dummy));
+
+ pr_debug("[BH] waiting ...\n");
+ status = wait_event_interruptible_timeout(priv->bh_wq, ({
+ rx = atomic_xchg(&priv->bh_rx, 0);
+ tx = atomic_xchg(&priv->bh_tx, 0);
+ term = atomic_xchg(&priv->bh_term, 0);
+ suspend = pending_tx ?
+ 0 : atomic_read(&priv->bh_suspend);
+ (rx || tx || term || suspend || priv->bh_error);
+ }), status);
+
+ pr_debug("[BH] - rx: %d, tx: %d, term: %d, suspend: %d, status: %ld\n",
+ rx, tx, term, suspend, status);
+
+ /* Did an error occur? */
+ if ((status < 0 && status != -ERESTARTSYS) ||
+ term || priv->bh_error) {
+ break;
+ }
+ if (!status) { /* wait_event timed out */
+ unsigned long timestamp = jiffies;
+ long timeout;
+ int pending = 0;
+ int i;
+
+ /* Check to see if we have any outstanding frames */
+ if (priv->hw_bufs_used && (!rx || !tx)) {
+ wiphy_warn(priv->hw->wiphy,
+ "Missed interrupt? (%d frames outstanding)\n",
+ priv->hw_bufs_used);
+ rx = 1;
+
+ /* Get a timestamp of "oldest" frame */
+ for (i = 0; i < 4; ++i)
+ pending += cw1200_queue_get_xmit_timestamp(
+ &priv->tx_queue[i],
+ &timestamp,
+ priv->pending_frame_id);
+
+ /* Check if frame transmission is timed out.
+ * Add an extra second with respect to possible
+ * interrupt loss.
+ */
+ timeout = timestamp +
+ WSM_CMD_LAST_CHANCE_TIMEOUT +
+ 1 * HZ -
+ jiffies;
+
+ /* And terminate BH thread if the frame is "stuck" */
+ if (pending && timeout < 0) {
+ wiphy_warn(priv->hw->wiphy,
+ "Timeout waiting for TX confirm (%d/%d pending, %ld vs %lu).\n",
+ priv->hw_bufs_used, pending,
+ timestamp, jiffies);
+ break;
+ }
+ } else if (!priv->device_can_sleep &&
+ !atomic_read(&priv->recent_scan)) {
+ pr_debug("[BH] Device wakedown. Timeout.\n");
+ cw1200_reg_write_16(priv,
+ ST90TDS_CONTROL_REG_ID, 0);
+ priv->device_can_sleep = true;
+ }
+ goto done;
+ } else if (suspend) {
+ pr_debug("[BH] Device suspend.\n");
+ if (priv->powersave_enabled) {
+ pr_debug("[BH] Device wakedown. Suspend.\n");
+ cw1200_reg_write_16(priv,
+ ST90TDS_CONTROL_REG_ID, 0);
+ priv->device_can_sleep = true;
+ }
+
+ atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED);
+ wake_up(&priv->bh_evt_wq);
+ status = wait_event_interruptible(priv->bh_wq,
+ CW1200_BH_RESUME == atomic_read(&priv->bh_suspend));
+ if (status < 0) {
+ wiphy_err(priv->hw->wiphy,
+ "Failed to wait for resume: %ld.\n",
+ status);
+ break;
+ }
+ pr_debug("[BH] Device resume.\n");
+ atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
+ wake_up(&priv->bh_evt_wq);
+ atomic_add(1, &priv->bh_rx);
+ goto done;
+ }
+
+ rx:
+ tx += pending_tx;
+ pending_tx = 0;
+
+ if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg))
+ break;
+
+ /* Don't bother trying to rx unless we have data to read */
+ if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) {
+ ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx);
+ if (ret < 0)
+ break;
+ /* Double up here if there's more data.. */
+ if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) {
+ ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx);
+ if (ret < 0)
+ break;
+ }
+ }
+
+ tx:
+ if (tx) {
+ tx = 0;
+
+ BUG_ON(priv->hw_bufs_used > priv->wsm_caps.input_buffers);
+ tx_burst = priv->wsm_caps.input_buffers - priv->hw_bufs_used;
+ tx_allowed = tx_burst > 0;
+
+ if (!tx_allowed) {
+ /* Buffers full. Ensure we process tx
+ * after we handle rx..
+ */
+ pending_tx = tx;
+ goto done_rx;
+ }
+ ret = cw1200_bh_tx_helper(priv, &pending_tx, &tx_burst);
+ if (ret < 0)
+ break;
+ if (ret > 0) /* More to transmit */
+ tx = ret;
+
+ /* Re-read ctrl reg */
+ if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg))
+ break;
+ }
+
+ done_rx:
+ if (priv->bh_error)
+ break;
+ if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
+ goto rx;
+ if (tx)
+ goto tx;
+
+ done:
+ /* Re-enable device interrupts */
+ priv->hwbus_ops->lock(priv->hwbus_priv);
+ __cw1200_irq_enable(priv, 1);
+ priv->hwbus_ops->unlock(priv->hwbus_priv);
+ }
+
+ /* Explicitly disable device interrupts */
+ priv->hwbus_ops->lock(priv->hwbus_priv);
+ __cw1200_irq_enable(priv, 0);
+ priv->hwbus_ops->unlock(priv->hwbus_priv);
+
+ if (!term) {
+ pr_err("[BH] Fatal error, exiting.\n");
+ priv->bh_error = 1;
+ /* TODO: schedule_work(recovery) */
+ }
+ return 0;
+}
diff --git a/drivers/net/wireless/cw1200/bh.h b/drivers/net/wireless/cw1200/bh.h
new file mode 100644
index 000000000000..af6a4853728f
--- /dev/null
+++ b/drivers/net/wireless/cw1200/bh.h
@@ -0,0 +1,28 @@
+/*
+ * Device handling thread interface for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 CW1200_BH_H
+#define CW1200_BH_H
+
+/* extern */ struct cw1200_common;
+
+int cw1200_register_bh(struct cw1200_common *priv);
+void cw1200_unregister_bh(struct cw1200_common *priv);
+void cw1200_irq_handler(struct cw1200_common *priv);
+void cw1200_bh_wakeup(struct cw1200_common *priv);
+int cw1200_bh_suspend(struct cw1200_common *priv);
+int cw1200_bh_resume(struct cw1200_common *priv);
+/* Must be called from BH thread. */
+void cw1200_enable_powersave(struct cw1200_common *priv,
+ bool enable);
+int wsm_release_tx_buffer(struct cw1200_common *priv, int count);
+
+#endif /* CW1200_BH_H */
diff --git a/drivers/net/wireless/cw1200/cw1200.h b/drivers/net/wireless/cw1200/cw1200.h
new file mode 100644
index 000000000000..243e96353d13
--- /dev/null
+++ b/drivers/net/wireless/cw1200/cw1200.h
@@ -0,0 +1,323 @@
+/*
+ * Common private data for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on the mac80211 Prism54 code, which is
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ *
+ * Based on the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ *
+ * 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 CW1200_H
+#define CW1200_H
+
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <net/mac80211.h>
+
+#include "queue.h"
+#include "wsm.h"
+#include "scan.h"
+#include "txrx.h"
+#include "pm.h"
+
+/* Forward declarations */
+struct hwbus_ops;
+struct task_struct;
+struct cw1200_debug_priv;
+struct firmware;
+
+#define CW1200_MAX_CTRL_FRAME_LEN (0x1000)
+
+#define CW1200_MAX_STA_IN_AP_MODE (5)
+#define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1)
+#define CW1200_LINK_ID_UAPSD (CW1200_MAX_STA_IN_AP_MODE + 2)
+#define CW1200_LINK_ID_MAX (CW1200_MAX_STA_IN_AP_MODE + 3)
+#define CW1200_MAX_REQUEUE_ATTEMPTS (5)
+
+#define CW1200_MAX_TID (8)
+
+#define CW1200_BLOCK_ACK_CNT (30)
+#define CW1200_BLOCK_ACK_THLD (800)
+#define CW1200_BLOCK_ACK_HIST (3)
+#define CW1200_BLOCK_ACK_INTERVAL (1 * HZ / CW1200_BLOCK_ACK_HIST)
+
+#define CW1200_JOIN_TIMEOUT (1 * HZ)
+#define CW1200_AUTH_TIMEOUT (5 * HZ)
+
+struct cw1200_ht_info {
+ struct ieee80211_sta_ht_cap ht_cap;
+ enum nl80211_channel_type channel_type;
+ u16 operation_mode;
+};
+
+/* Please keep order */
+enum cw1200_join_status {
+ CW1200_JOIN_STATUS_PASSIVE = 0,
+ CW1200_JOIN_STATUS_MONITOR,
+ CW1200_JOIN_STATUS_JOINING,
+ CW1200_JOIN_STATUS_PRE_STA,
+ CW1200_JOIN_STATUS_STA,
+ CW1200_JOIN_STATUS_IBSS,
+ CW1200_JOIN_STATUS_AP,
+};
+
+enum cw1200_link_status {
+ CW1200_LINK_OFF,
+ CW1200_LINK_RESERVE,
+ CW1200_LINK_SOFT,
+ CW1200_LINK_HARD,
+ CW1200_LINK_RESET,
+ CW1200_LINK_RESET_REMAP,
+};
+
+extern int cw1200_power_mode;
+extern const char * const cw1200_fw_types[];
+
+struct cw1200_link_entry {
+ unsigned long timestamp;
+ enum cw1200_link_status status;
+ enum cw1200_link_status prev_status;
+ u8 mac[ETH_ALEN];
+ u8 buffered[CW1200_MAX_TID];
+ struct sk_buff_head rx_queue;
+};
+
+struct cw1200_common {
+ /* interfaces to the rest of the stack */
+ struct ieee80211_hw *hw;
+ struct ieee80211_vif *vif;
+ struct device *pdev;
+
+ /* Statistics */
+ struct ieee80211_low_level_stats stats;
+
+ /* Our macaddr */
+ u8 mac_addr[ETH_ALEN];
+
+ /* Hardware interface */
+ const struct hwbus_ops *hwbus_ops;
+ struct hwbus_priv *hwbus_priv;
+
+ /* Hardware information */
+ enum {
+ HIF_9000_SILICON_VERSATILE = 0,
+ HIF_8601_VERSATILE,
+ HIF_8601_SILICON,
+ } hw_type;
+ enum {
+ CW1200_HW_REV_CUT10 = 10,
+ CW1200_HW_REV_CUT11 = 11,
+ CW1200_HW_REV_CUT20 = 20,
+ CW1200_HW_REV_CUT22 = 22,
+ CW1X60_HW_REV = 40,
+ } hw_revision;
+ int hw_refclk;
+ bool hw_have_5ghz;
+ const struct firmware *sdd;
+ char *sdd_path;
+
+ struct cw1200_debug_priv *debug;
+
+ struct workqueue_struct *workqueue;
+ struct mutex conf_mutex;
+
+ struct cw1200_queue tx_queue[4];
+ struct cw1200_queue_stats tx_queue_stats;
+ int tx_burst_idx;
+
+ /* firmware/hardware info */
+ unsigned int tx_hdr_len;
+
+ /* Radio data */
+ int output_power;
+
+ /* BBP/MAC state */
+ struct ieee80211_rate *rates;
+ struct ieee80211_rate *mcs_rates;
+ struct ieee80211_channel *channel;
+ struct wsm_edca_params edca;
+ struct wsm_tx_queue_params tx_queue_params;
+ struct wsm_mib_association_mode association_mode;
+ struct wsm_set_bss_params bss_params;
+ struct cw1200_ht_info ht_info;
+ struct wsm_set_pm powersave_mode;
+ struct wsm_set_pm firmware_ps_mode;
+ int cqm_rssi_thold;
+ unsigned cqm_rssi_hyst;
+ bool cqm_use_rssi;
+ int cqm_beacon_loss_count;
+ int channel_switch_in_progress;
+ wait_queue_head_t channel_switch_done;
+ u8 long_frame_max_tx_count;
+ u8 short_frame_max_tx_count;
+ int mode;
+ bool enable_beacon;
+ int beacon_int;
+ bool listening;
+ struct wsm_rx_filter rx_filter;
+ struct wsm_mib_multicast_filter multicast_filter;
+ bool has_multicast_subscription;
+ bool disable_beacon_filter;
+ struct work_struct update_filtering_work;
+ struct work_struct set_beacon_wakeup_period_work;
+
+ u8 ba_rx_tid_mask;
+ u8 ba_tx_tid_mask;
+
+ struct cw1200_pm_state pm_state;
+
+ struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo;
+ struct wsm_uapsd_info uapsd_info;
+ bool setbssparams_done;
+ bool bt_present;
+ u8 conf_listen_interval;
+ u32 listen_interval;
+ u32 erp_info;
+ u32 rts_threshold;
+
+ /* BH */
+ atomic_t bh_rx;
+ atomic_t bh_tx;
+ atomic_t bh_term;
+ atomic_t bh_suspend;
+
+ struct workqueue_struct *bh_workqueue;
+ struct work_struct bh_work;
+
+ int bh_error;
+ wait_queue_head_t bh_wq;
+ wait_queue_head_t bh_evt_wq;
+ u8 buf_id_tx;
+ u8 buf_id_rx;
+ u8 wsm_rx_seq;
+ u8 wsm_tx_seq;
+ int hw_bufs_used;
+ bool powersave_enabled;
+ bool device_can_sleep;
+
+ /* Scan status */
+ struct cw1200_scan scan;
+ /* Keep cw1200 awake (WUP = 1) 1 second after each scan to avoid
+ * FW issue with sleeping/waking up.
+ */
+ atomic_t recent_scan;
+ struct delayed_work clear_recent_scan_work;
+
+ /* WSM */
+ struct wsm_startup_ind wsm_caps;
+ struct mutex wsm_cmd_mux;
+ struct wsm_buf wsm_cmd_buf;
+ struct wsm_cmd wsm_cmd;
+ wait_queue_head_t wsm_cmd_wq;
+ wait_queue_head_t wsm_startup_done;
+ int firmware_ready;
+ atomic_t tx_lock;
+
+ /* WSM debug */
+ int wsm_enable_wsm_dumps;
+
+ /* WSM Join */
+ enum cw1200_join_status join_status;
+ u32 pending_frame_id;
+ bool join_pending;
+ struct delayed_work join_timeout;
+ struct work_struct unjoin_work;
+ struct work_struct join_complete_work;
+ int join_complete_status;
+ int join_dtim_period;
+ bool delayed_unjoin;
+
+ /* TX/RX and security */
+ s8 wep_default_key_id;
+ struct work_struct wep_key_work;
+ u32 key_map;
+ struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1];
+
+ /* AP powersave */
+ u32 link_id_map;
+ struct cw1200_link_entry link_id_db[CW1200_MAX_STA_IN_AP_MODE];
+ struct work_struct link_id_work;
+ struct delayed_work link_id_gc_work;
+ u32 sta_asleep_mask;
+ u32 pspoll_mask;
+ bool aid0_bit_set;
+ spinlock_t ps_state_lock; /* Protect power save state */
+ bool buffered_multicasts;
+ bool tx_multicast;
+ struct work_struct set_tim_work;
+ struct work_struct set_cts_work;
+ struct work_struct multicast_start_work;
+ struct work_struct multicast_stop_work;
+ struct timer_list mcast_timeout;
+
+ /* WSM events and CQM implementation */
+ spinlock_t event_queue_lock; /* Protect event queue */
+ struct list_head event_queue;
+ struct work_struct event_handler;
+
+ struct delayed_work bss_loss_work;
+ spinlock_t bss_loss_lock; /* Protect BSS loss state */
+ int bss_loss_state;
+ int bss_loss_confirm_id;
+ int delayed_link_loss;
+ struct work_struct bss_params_work;
+
+ /* TX rate policy cache */
+ struct tx_policy_cache tx_policy_cache;
+ struct work_struct tx_policy_upload_work;
+
+ /* legacy PS mode switch in suspend */
+ int ps_mode_switch_in_progress;
+ wait_queue_head_t ps_mode_switch_done;
+
+ /* Workaround for WFD testcase 6.1.10*/
+ struct work_struct linkid_reset_work;
+ u8 action_frame_sa[ETH_ALEN];
+ u8 action_linkid;
+};
+
+struct cw1200_sta_priv {
+ int link_id;
+};
+
+/* interfaces for the drivers */
+int cw1200_core_probe(const struct hwbus_ops *hwbus_ops,
+ struct hwbus_priv *hwbus,
+ struct device *pdev,
+ struct cw1200_common **pself,
+ int ref_clk, const u8 *macaddr,
+ const char *sdd_path, bool have_5ghz);
+void cw1200_core_release(struct cw1200_common *self);
+
+#define FWLOAD_BLOCK_SIZE (1024)
+
+static inline int cw1200_is_ht(const struct cw1200_ht_info *ht_info)
+{
+ return ht_info->channel_type != NL80211_CHAN_NO_HT;
+}
+
+static inline int cw1200_ht_greenfield(const struct cw1200_ht_info *ht_info)
+{
+ return cw1200_is_ht(ht_info) &&
+ (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
+ !(ht_info->operation_mode &
+ IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+}
+
+static inline int cw1200_ht_ampdu_density(const struct cw1200_ht_info *ht_info)
+{
+ if (!cw1200_is_ht(ht_info))
+ return 0;
+ return ht_info->ht_cap.ampdu_density;
+}
+
+#endif /* CW1200_H */
diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/cw1200/cw1200_sdio.c
new file mode 100644
index 000000000000..ebdcdf44f155
--- /dev/null
+++ b/drivers/net/wireless/cw1200/cw1200_sdio.c
@@ -0,0 +1,425 @@
+/*
+ * Mac80211 SDIO driver for ST-Ericsson CW1200 device
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/gpio.h>
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <net/mac80211.h>
+
+#include "cw1200.h"
+#include "hwbus.h"
+#include <linux/platform_data/net-cw1200.h>
+#include "hwio.h"
+
+MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
+MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver");
+MODULE_LICENSE("GPL");
+
+#define SDIO_BLOCK_SIZE (512)
+
+/* Default platform data for Sagrad modules */
+static struct cw1200_platform_data_sdio sagrad_109x_evk_platform_data = {
+ .ref_clk = 38400,
+ .have_5ghz = false,
+ .sdd_file = "sdd_sagrad_1091_1098.bin",
+};
+
+/* Allow platform data to be overridden */
+static struct cw1200_platform_data_sdio *global_plat_data = &sagrad_109x_evk_platform_data;
+
+void __init cw1200_sdio_set_platform_data(struct cw1200_platform_data_sdio *pdata)
+{
+ global_plat_data = pdata;
+}
+
+struct hwbus_priv {
+ struct sdio_func *func;
+ struct cw1200_common *core;
+ const struct cw1200_platform_data_sdio *pdata;
+};
+
+#ifndef SDIO_VENDOR_ID_STE
+#define SDIO_VENDOR_ID_STE 0x0020
+#endif
+
+#ifndef SDIO_DEVICE_ID_STE_CW1200
+#define SDIO_DEVICE_ID_STE_CW1200 0x2280
+#endif
+
+static const struct sdio_device_id cw1200_sdio_ids[] = {
+ { SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200) },
+ { /* end: all zeroes */ },
+};
+
+/* hwbus_ops implemetation */
+
+static int cw1200_sdio_memcpy_fromio(struct hwbus_priv *self,
+ unsigned int addr,
+ void *dst, int count)
+{
+ return sdio_memcpy_fromio(self->func, dst, addr, count);
+}
+
+static int cw1200_sdio_memcpy_toio(struct hwbus_priv *self,
+ unsigned int addr,
+ const void *src, int count)
+{
+ return sdio_memcpy_toio(self->func, addr, (void *)src, count);
+}
+
+static void cw1200_sdio_lock(struct hwbus_priv *self)
+{
+ sdio_claim_host(self->func);
+}
+
+static void cw1200_sdio_unlock(struct hwbus_priv *self)
+{
+ sdio_release_host(self->func);
+}
+
+static void cw1200_sdio_irq_handler(struct sdio_func *func)
+{
+ struct hwbus_priv *self = sdio_get_drvdata(func);
+
+ /* note: sdio_host already claimed here. */
+ if (self->core)
+ cw1200_irq_handler(self->core);
+}
+
+static irqreturn_t cw1200_gpio_hardirq(int irq, void *dev_id)
+{
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t cw1200_gpio_irq(int irq, void *dev_id)
+{
+ struct hwbus_priv *self = dev_id;
+
+ if (self->core) {
+ sdio_claim_host(self->func);
+ cw1200_irq_handler(self->core);
+ sdio_release_host(self->func);
+ return IRQ_HANDLED;
+ } else {
+ return IRQ_NONE;
+ }
+}
+
+static int cw1200_request_irq(struct hwbus_priv *self)
+{
+ int ret;
+ u8 cccr;
+
+ cccr = sdio_f0_readb(self->func, SDIO_CCCR_IENx, &ret);
+ if (WARN_ON(ret))
+ goto err;
+
+ /* Master interrupt enable ... */
+ cccr |= BIT(0);
+
+ /* ... for our function */
+ cccr |= BIT(self->func->num);
+
+ sdio_f0_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret);
+ if (WARN_ON(ret))
+ goto err;
+
+ ret = enable_irq_wake(self->pdata->irq);
+ if (WARN_ON(ret))
+ goto err;
+
+ /* Request the IRQ */
+ ret = request_threaded_irq(self->pdata->irq, cw1200_gpio_hardirq,
+ cw1200_gpio_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "cw1200_wlan_irq", self);
+ if (WARN_ON(ret))
+ goto err;
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static int cw1200_sdio_irq_subscribe(struct hwbus_priv *self)
+{
+ int ret = 0;
+
+ pr_debug("SW IRQ subscribe\n");
+ sdio_claim_host(self->func);
+ if (self->pdata->irq)
+ ret = cw1200_request_irq(self);
+ else
+ ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler);
+
+ sdio_release_host(self->func);
+ return ret;
+}
+
+static int cw1200_sdio_irq_unsubscribe(struct hwbus_priv *self)
+{
+ int ret = 0;
+
+ pr_debug("SW IRQ unsubscribe\n");
+
+ if (self->pdata->irq) {
+ disable_irq_wake(self->pdata->irq);
+ free_irq(self->pdata->irq, self);
+ } else {
+ sdio_claim_host(self->func);
+ ret = sdio_release_irq(self->func);
+ sdio_release_host(self->func);
+ }
+ return ret;
+}
+
+static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata)
+{
+ if (pdata->reset) {
+ gpio_set_value(pdata->reset, 0);
+ msleep(30); /* Min is 2 * CLK32K cycles */
+ gpio_free(pdata->reset);
+ }
+
+ if (pdata->power_ctrl)
+ pdata->power_ctrl(pdata, false);
+ if (pdata->clk_ctrl)
+ pdata->clk_ctrl(pdata, false);
+
+ return 0;
+}
+
+static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata)
+{
+ /* Ensure I/Os are pulled low */
+ if (pdata->reset) {
+ gpio_request(pdata->reset, "cw1200_wlan_reset");
+ gpio_direction_output(pdata->reset, 0);
+ }
+ if (pdata->powerup) {
+ gpio_request(pdata->powerup, "cw1200_wlan_powerup");
+ gpio_direction_output(pdata->powerup, 0);
+ }
+ if (pdata->reset || pdata->powerup)
+ msleep(10); /* Settle time? */
+
+ /* Enable 3v3 and 1v8 to hardware */
+ if (pdata->power_ctrl) {
+ if (pdata->power_ctrl(pdata, true)) {
+ pr_err("power_ctrl() failed!\n");
+ return -1;
+ }
+ }
+
+ /* Enable CLK32K */
+ if (pdata->clk_ctrl) {
+ if (pdata->clk_ctrl(pdata, true)) {
+ pr_err("clk_ctrl() failed!\n");
+ return -1;
+ }
+ msleep(10); /* Delay until clock is stable for 2 cycles */
+ }
+
+ /* Enable POWERUP signal */
+ if (pdata->powerup) {
+ gpio_set_value(pdata->powerup, 1);
+ msleep(250); /* or more..? */
+ }
+ /* Enable RSTn signal */
+ if (pdata->reset) {
+ gpio_set_value(pdata->reset, 1);
+ msleep(50); /* Or more..? */
+ }
+ return 0;
+}
+
+static size_t cw1200_sdio_align_size(struct hwbus_priv *self, size_t size)
+{
+ if (self->pdata->no_nptb)
+ size = round_up(size, SDIO_BLOCK_SIZE);
+ else
+ size = sdio_align_size(self->func, size);
+
+ return size;
+}
+
+static int cw1200_sdio_pm(struct hwbus_priv *self, bool suspend)
+{
+ int ret = 0;
+
+ if (self->pdata->irq)
+ ret = irq_set_irq_wake(self->pdata->irq, suspend);
+ return ret;
+}
+
+static struct hwbus_ops cw1200_sdio_hwbus_ops = {
+ .hwbus_memcpy_fromio = cw1200_sdio_memcpy_fromio,
+ .hwbus_memcpy_toio = cw1200_sdio_memcpy_toio,
+ .lock = cw1200_sdio_lock,
+ .unlock = cw1200_sdio_unlock,
+ .align_size = cw1200_sdio_align_size,
+ .power_mgmt = cw1200_sdio_pm,
+};
+
+/* Probe Function to be called by SDIO stack when device is discovered */
+static int cw1200_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct hwbus_priv *self;
+ int status;
+
+ pr_info("cw1200_wlan_sdio: Probe called\n");
+
+ /* We are only able to handle the wlan function */
+ if (func->num != 0x01)
+ return -ENODEV;
+
+ self = kzalloc(sizeof(*self), GFP_KERNEL);
+ if (!self) {
+ pr_err("Can't allocate SDIO hwbus_priv.\n");
+ return -ENOMEM;
+ }
+
+ func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+
+ self->pdata = global_plat_data; /* FIXME */
+ self->func = func;
+ sdio_set_drvdata(func, self);
+ sdio_claim_host(func);
+ sdio_enable_func(func);
+ sdio_release_host(func);
+
+ status = cw1200_sdio_irq_subscribe(self);
+
+ status = cw1200_core_probe(&cw1200_sdio_hwbus_ops,
+ self, &func->dev, &self->core,
+ self->pdata->ref_clk,
+ self->pdata->macaddr,
+ self->pdata->sdd_file,
+ self->pdata->have_5ghz);
+ if (status) {
+ cw1200_sdio_irq_unsubscribe(self);
+ sdio_claim_host(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+ sdio_set_drvdata(func, NULL);
+ kfree(self);
+ }
+
+ return status;
+}
+
+/* Disconnect Function to be called by SDIO stack when
+ * device is disconnected
+ */
+static void cw1200_sdio_disconnect(struct sdio_func *func)
+{
+ struct hwbus_priv *self = sdio_get_drvdata(func);
+
+ if (self) {
+ cw1200_sdio_irq_unsubscribe(self);
+ if (self->core) {
+ cw1200_core_release(self->core);
+ self->core = NULL;
+ }
+ sdio_claim_host(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+ sdio_set_drvdata(func, NULL);
+ kfree(self);
+ }
+}
+
+#ifdef CONFIG_PM
+static int cw1200_sdio_suspend(struct device *dev)
+{
+ int ret;
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct hwbus_priv *self = sdio_get_drvdata(func);
+
+ if (!cw1200_can_suspend(self->core))
+ return -EAGAIN;
+
+ /* Notify SDIO that CW1200 will remain powered during suspend */
+ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+ if (ret)
+ pr_err("Error setting SDIO pm flags: %i\n", ret);
+
+ return ret;
+}
+
+static int cw1200_sdio_resume(struct device *dev)
+{
+ return 0;
+}
+
+static const struct dev_pm_ops cw1200_pm_ops = {
+ .suspend = cw1200_sdio_suspend,
+ .resume = cw1200_sdio_resume,
+};
+#endif
+
+static struct sdio_driver sdio_driver = {
+ .name = "cw1200_wlan_sdio",
+ .id_table = cw1200_sdio_ids,
+ .probe = cw1200_sdio_probe,
+ .remove = cw1200_sdio_disconnect,
+#ifdef CONFIG_PM
+ .drv = {
+ .pm = &cw1200_pm_ops,
+ }
+#endif
+};
+
+/* Init Module function -> Called by insmod */
+static int __init cw1200_sdio_init(void)
+{
+ const struct cw1200_platform_data_sdio *pdata;
+ int ret;
+
+ /* FIXME -- this won't support multiple devices */
+ pdata = global_plat_data;
+
+ if (cw1200_sdio_on(pdata)) {
+ ret = -1;
+ goto err;
+ }
+
+ ret = sdio_register_driver(&sdio_driver);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ cw1200_sdio_off(pdata);
+ return ret;
+}
+
+/* Called at Driver Unloading */
+static void __exit cw1200_sdio_exit(void)
+{
+ const struct cw1200_platform_data_sdio *pdata;
+
+ /* FIXME -- this won't support multiple devices */
+ pdata = global_plat_data;
+ sdio_unregister_driver(&sdio_driver);
+ cw1200_sdio_off(pdata);
+}
+
+
+module_init(cw1200_sdio_init);
+module_exit(cw1200_sdio_exit);
diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c
new file mode 100644
index 000000000000..953bd1904d3d
--- /dev/null
+++ b/drivers/net/wireless/cw1200/cw1200_spi.c
@@ -0,0 +1,463 @@
+/*
+ * Mac80211 SPI driver for ST-Ericsson CW1200 device
+ *
+ * Copyright (c) 2011, Sagrad Inc.
+ * Author: Solomon Peachy <speachy@sagrad.com>
+ *
+ * Based on cw1200_sdio.c
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/gpio.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <net/mac80211.h>
+
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+
+#include "cw1200.h"
+#include "hwbus.h"
+#include <linux/platform_data/net-cw1200.h>
+#include "hwio.h"
+
+MODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>");
+MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SPI driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:cw1200_wlan_spi");
+
+/* #define SPI_DEBUG */
+
+struct hwbus_priv {
+ struct spi_device *func;
+ struct cw1200_common *core;
+ const struct cw1200_platform_data_spi *pdata;
+ spinlock_t lock; /* Serialize all bus operations */
+ int claimed;
+};
+
+#define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2)
+#define SET_WRITE 0x7FFF /* usage: and operation */
+#define SET_READ 0x8000 /* usage: or operation */
+
+/* Notes on byte ordering:
+ LE: B0 B1 B2 B3
+ BE: B3 B2 B1 B0
+
+ Hardware expects 32-bit data to be written as 16-bit BE words:
+
+ B1 B0 B3 B2
+*/
+
+static int cw1200_spi_memcpy_fromio(struct hwbus_priv *self,
+ unsigned int addr,
+ void *dst, int count)
+{
+ int ret, i;
+ uint16_t regaddr;
+ struct spi_message m;
+
+ struct spi_transfer t_addr = {
+ .tx_buf = &regaddr,
+ .len = sizeof(regaddr),
+ };
+ struct spi_transfer t_msg = {
+ .rx_buf = dst,
+ .len = count,
+ };
+
+ regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
+ regaddr |= SET_READ;
+ regaddr |= (count>>1);
+ regaddr = cpu_to_le16(regaddr);
+
+#ifdef SPI_DEBUG
+ pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr,
+ le16_to_cpu(regaddr));
+#endif
+
+#if defined(__LITTLE_ENDIAN)
+ /* We have to byteswap if the SPI bus is limited to 8b operation */
+ if (self->func->bits_per_word == 8)
+#endif
+ regaddr = swab16(regaddr);
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t_addr, &m);
+ spi_message_add_tail(&t_msg, &m);
+ ret = spi_sync(self->func, &m);
+
+#ifdef SPI_DEBUG
+ pr_info("READ : ");
+ for (i = 0; i < t_addr.len; i++)
+ printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
+ printk(" : ");
+ for (i = 0; i < t_msg.len; i++)
+ printk("%02x ", ((u8 *)t_msg.rx_buf)[i]);
+ printk("\n");
+#endif
+
+#if defined(__LITTLE_ENDIAN)
+ /* We have to byteswap if the SPI bus is limited to 8b operation */
+ if (self->func->bits_per_word == 8)
+#endif
+ {
+ uint16_t *buf = (uint16_t *)dst;
+ for (i = 0; i < ((count + 1) >> 1); i++)
+ buf[i] = swab16(buf[i]);
+ }
+
+ return ret;
+}
+
+static int cw1200_spi_memcpy_toio(struct hwbus_priv *self,
+ unsigned int addr,
+ const void *src, int count)
+{
+ int rval, i;
+ uint16_t regaddr;
+ struct spi_transfer t_addr = {
+ .tx_buf = &regaddr,
+ .len = sizeof(regaddr),
+ };
+ struct spi_transfer t_msg = {
+ .tx_buf = src,
+ .len = count,
+ };
+ struct spi_message m;
+
+ regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
+ regaddr &= SET_WRITE;
+ regaddr |= (count>>1);
+ regaddr = cpu_to_le16(regaddr);
+
+#ifdef SPI_DEBUG
+ pr_info("WRITE: %04d to 0x%02x (%04x)\n", count, addr,
+ le16_to_cpu(regaddr));
+#endif
+
+#if defined(__LITTLE_ENDIAN)
+ /* We have to byteswap if the SPI bus is limited to 8b operation */
+ if (self->func->bits_per_word == 8)
+#endif
+ {
+ uint16_t *buf = (uint16_t *)src;
+ regaddr = swab16(regaddr);
+ for (i = 0; i < ((count + 1) >> 1); i++)
+ buf[i] = swab16(buf[i]);
+ }
+
+#ifdef SPI_DEBUG
+ pr_info("WRITE: ");
+ for (i = 0; i < t_addr.len; i++)
+ printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
+ printk(" : ");
+ for (i = 0; i < t_msg.len; i++)
+ printk("%02x ", ((u8 *)t_msg.tx_buf)[i]);
+ printk("\n");
+#endif
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t_addr, &m);
+ spi_message_add_tail(&t_msg, &m);
+ rval = spi_sync(self->func, &m);
+
+#ifdef SPI_DEBUG
+ pr_info("WROTE: %d\n", m.actual_length);
+#endif
+
+#if defined(__LITTLE_ENDIAN)
+ /* We have to byteswap if the SPI bus is limited to 8b operation */
+ if (self->func->bits_per_word == 8)
+#endif
+ {
+ uint16_t *buf = (uint16_t *)src;
+ for (i = 0; i < ((count + 1) >> 1); i++)
+ buf[i] = swab16(buf[i]);
+ }
+ return rval;
+}
+
+static void cw1200_spi_lock(struct hwbus_priv *self)
+{
+ unsigned long flags;
+
+ might_sleep();
+
+ spin_lock_irqsave(&self->lock, flags);
+ while (1) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (!self->claimed)
+ break;
+ spin_unlock_irqrestore(&self->lock, flags);
+ schedule();
+ spin_lock_irqsave(&self->lock, flags);
+ }
+ set_current_state(TASK_RUNNING);
+ self->claimed = 1;
+ spin_unlock_irqrestore(&self->lock, flags);
+
+ return;
+}
+
+static void cw1200_spi_unlock(struct hwbus_priv *self)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&self->lock, flags);
+ self->claimed = 0;
+ spin_unlock_irqrestore(&self->lock, flags);
+ return;
+}
+
+static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id)
+{
+ struct hwbus_priv *self = dev_id;
+
+ if (self->core) {
+ cw1200_irq_handler(self->core);
+ return IRQ_HANDLED;
+ } else {
+ return IRQ_NONE;
+ }
+}
+
+static int cw1200_spi_irq_subscribe(struct hwbus_priv *self)
+{
+ int ret;
+
+ pr_debug("SW IRQ subscribe\n");
+
+ ret = request_any_context_irq(self->func->irq, cw1200_spi_irq_handler,
+ IRQF_TRIGGER_HIGH,
+ "cw1200_wlan_irq", self);
+ if (WARN_ON(ret < 0))
+ goto exit;
+
+ ret = enable_irq_wake(self->func->irq);
+ if (WARN_ON(ret))
+ goto free_irq;
+
+ return 0;
+
+free_irq:
+ free_irq(self->func->irq, self);
+exit:
+ return ret;
+}
+
+static int cw1200_spi_irq_unsubscribe(struct hwbus_priv *self)
+{
+ int ret = 0;
+
+ pr_debug("SW IRQ unsubscribe\n");
+ disable_irq_wake(self->func->irq);
+ free_irq(self->func->irq, self);
+
+ return ret;
+}
+
+static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata)
+{
+ if (pdata->reset) {
+ gpio_set_value(pdata->reset, 0);
+ msleep(30); /* Min is 2 * CLK32K cycles */
+ gpio_free(pdata->reset);
+ }
+
+ if (pdata->power_ctrl)
+ pdata->power_ctrl(pdata, false);
+ if (pdata->clk_ctrl)
+ pdata->clk_ctrl(pdata, false);
+
+ return 0;
+}
+
+static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata)
+{
+ /* Ensure I/Os are pulled low */
+ if (pdata->reset) {
+ gpio_request(pdata->reset, "cw1200_wlan_reset");
+ gpio_direction_output(pdata->reset, 0);
+ }
+ if (pdata->powerup) {
+ gpio_request(pdata->powerup, "cw1200_wlan_powerup");
+ gpio_direction_output(pdata->powerup, 0);
+ }
+ if (pdata->reset || pdata->powerup)
+ msleep(10); /* Settle time? */
+
+ /* Enable 3v3 and 1v8 to hardware */
+ if (pdata->power_ctrl) {
+ if (pdata->power_ctrl(pdata, true)) {
+ pr_err("power_ctrl() failed!\n");
+ return -1;
+ }
+ }
+
+ /* Enable CLK32K */
+ if (pdata->clk_ctrl) {
+ if (pdata->clk_ctrl(pdata, true)) {
+ pr_err("clk_ctrl() failed!\n");
+ return -1;
+ }
+ msleep(10); /* Delay until clock is stable for 2 cycles */
+ }
+
+ /* Enable POWERUP signal */
+ if (pdata->powerup) {
+ gpio_set_value(pdata->powerup, 1);
+ msleep(250); /* or more..? */
+ }
+ /* Enable RSTn signal */
+ if (pdata->reset) {
+ gpio_set_value(pdata->reset, 1);
+ msleep(50); /* Or more..? */
+ }
+ return 0;
+}
+
+static size_t cw1200_spi_align_size(struct hwbus_priv *self, size_t size)
+{
+ return size & 1 ? size + 1 : size;
+}
+
+static int cw1200_spi_pm(struct hwbus_priv *self, bool suspend)
+{
+ return irq_set_irq_wake(self->func->irq, suspend);
+}
+
+static struct hwbus_ops cw1200_spi_hwbus_ops = {
+ .hwbus_memcpy_fromio = cw1200_spi_memcpy_fromio,
+ .hwbus_memcpy_toio = cw1200_spi_memcpy_toio,
+ .lock = cw1200_spi_lock,
+ .unlock = cw1200_spi_unlock,
+ .align_size = cw1200_spi_align_size,
+ .power_mgmt = cw1200_spi_pm,
+};
+
+/* Probe Function to be called by SPI stack when device is discovered */
+static int cw1200_spi_probe(struct spi_device *func)
+{
+ const struct cw1200_platform_data_spi *plat_data =
+ func->dev.platform_data;
+ struct hwbus_priv *self;
+ int status;
+
+ /* Sanity check speed */
+ if (func->max_speed_hz > 52000000)
+ func->max_speed_hz = 52000000;
+ if (func->max_speed_hz < 1000000)
+ func->max_speed_hz = 1000000;
+
+ /* Fix up transfer size */
+ if (plat_data->spi_bits_per_word)
+ func->bits_per_word = plat_data->spi_bits_per_word;
+ if (!func->bits_per_word)
+ func->bits_per_word = 16;
+
+ /* And finally.. */
+ func->mode = SPI_MODE_0;
+
+ pr_info("cw1200_wlan_spi: Probe called (CS %d M %d BPW %d CLK %d)\n",
+ func->chip_select, func->mode, func->bits_per_word,
+ func->max_speed_hz);
+
+ if (cw1200_spi_on(plat_data)) {
+ pr_err("spi_on() failed!\n");
+ return -1;
+ }
+
+ if (spi_setup(func)) {
+ pr_err("spi_setup() failed!\n");
+ return -1;
+ }
+
+ self = kzalloc(sizeof(*self), GFP_KERNEL);
+ if (!self) {
+ pr_err("Can't allocate SPI hwbus_priv.");
+ return -ENOMEM;
+ }
+
+ self->pdata = plat_data;
+ self->func = func;
+ spin_lock_init(&self->lock);
+
+ spi_set_drvdata(func, self);
+
+ status = cw1200_spi_irq_subscribe(self);
+
+ status = cw1200_core_probe(&cw1200_spi_hwbus_ops,
+ self, &func->dev, &self->core,
+ self->pdata->ref_clk,
+ self->pdata->macaddr,
+ self->pdata->sdd_file,
+ self->pdata->have_5ghz);
+
+ if (status) {
+ cw1200_spi_irq_unsubscribe(self);
+ cw1200_spi_off(plat_data);
+ kfree(self);
+ }
+
+ return status;
+}
+
+/* Disconnect Function to be called by SPI stack when device is disconnected */
+static int cw1200_spi_disconnect(struct spi_device *func)
+{
+ struct hwbus_priv *self = spi_get_drvdata(func);
+
+ if (self) {
+ cw1200_spi_irq_unsubscribe(self);
+ if (self->core) {
+ cw1200_core_release(self->core);
+ self->core = NULL;
+ }
+ kfree(self);
+ }
+ cw1200_spi_off(func->dev.platform_data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int cw1200_spi_suspend(struct device *dev, pm_message_t state)
+{
+ struct hwbus_priv *self = spi_get_drvdata(to_spi_device(dev));
+
+ if (!cw1200_can_suspend(self->core))
+ return -EAGAIN;
+
+ /* XXX notify host that we have to keep CW1200 powered on? */
+ return 0;
+}
+
+static int cw1200_spi_resume(struct device *dev)
+{
+ return 0;
+}
+#endif
+
+static struct spi_driver spi_driver = {
+ .probe = cw1200_spi_probe,
+ .remove = cw1200_spi_disconnect,
+ .driver = {
+ .name = "cw1200_wlan_spi",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .suspend = cw1200_spi_suspend,
+ .resume = cw1200_spi_resume,
+#endif
+ },
+};
+
+module_spi_driver(spi_driver);
diff --git a/drivers/net/wireless/cw1200/debug.c b/drivers/net/wireless/cw1200/debug.c
new file mode 100644
index 000000000000..e323b4d54338
--- /dev/null
+++ b/drivers/net/wireless/cw1200/debug.c
@@ -0,0 +1,428 @@
+/*
+ * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
+ * DebugFS code
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/debugfs.h>
+#include <linux/seq_file.h>
+#include "cw1200.h"
+#include "debug.h"
+#include "fwio.h"
+
+/* join_status */
+static const char * const cw1200_debug_join_status[] = {
+ "passive",
+ "monitor",
+ "station (joining)",
+ "station (not authenticated yet)",
+ "station",
+ "adhoc",
+ "access point",
+};
+
+/* WSM_JOIN_PREAMBLE_... */
+static const char * const cw1200_debug_preamble[] = {
+ "long",
+ "short",
+ "long on 1 and 2 Mbps",
+};
+
+
+static const char * const cw1200_debug_link_id[] = {
+ "OFF",
+ "REQ",
+ "SOFT",
+ "HARD",
+};
+
+static const char *cw1200_debug_mode(int mode)
+{
+ switch (mode) {
+ case NL80211_IFTYPE_UNSPECIFIED:
+ return "unspecified";
+ case NL80211_IFTYPE_MONITOR:
+ return "monitor";
+ case NL80211_IFTYPE_STATION:
+ return "station";
+ case NL80211_IFTYPE_ADHOC:
+ return "adhoc";
+ case NL80211_IFTYPE_MESH_POINT:
+ return "mesh point";
+ case NL80211_IFTYPE_AP:
+ return "access point";
+ case NL80211_IFTYPE_P2P_CLIENT:
+ return "p2p client";
+ case NL80211_IFTYPE_P2P_GO:
+ return "p2p go";
+ default:
+ return "unsupported";
+ }
+}
+
+static void cw1200_queue_status_show(struct seq_file *seq,
+ struct cw1200_queue *q)
+{
+ int i;
+ seq_printf(seq, "Queue %d:\n", q->queue_id);
+ seq_printf(seq, " capacity: %zu\n", q->capacity);
+ seq_printf(seq, " queued: %zu\n", q->num_queued);
+ seq_printf(seq, " pending: %zu\n", q->num_pending);
+ seq_printf(seq, " sent: %zu\n", q->num_sent);
+ seq_printf(seq, " locked: %s\n", q->tx_locked_cnt ? "yes" : "no");
+ seq_printf(seq, " overfull: %s\n", q->overfull ? "yes" : "no");
+ seq_puts(seq, " link map: 0-> ");
+ for (i = 0; i < q->stats->map_capacity; ++i)
+ seq_printf(seq, "%.2d ", q->link_map_cache[i]);
+ seq_printf(seq, "<-%zu\n", q->stats->map_capacity);
+}
+
+static void cw1200_debug_print_map(struct seq_file *seq,
+ struct cw1200_common *priv,
+ const char *label,
+ u32 map)
+{
+ int i;
+ seq_printf(seq, "%s0-> ", label);
+ for (i = 0; i < priv->tx_queue_stats.map_capacity; ++i)
+ seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : "..");
+ seq_printf(seq, "<-%zu\n", priv->tx_queue_stats.map_capacity - 1);
+}
+
+static int cw1200_status_show(struct seq_file *seq, void *v)
+{
+ int i;
+ struct list_head *item;
+ struct cw1200_common *priv = seq->private;
+ struct cw1200_debug_priv *d = priv->debug;
+
+ seq_puts(seq, "CW1200 Wireless LAN driver status\n");
+ seq_printf(seq, "Hardware: %d.%d\n",
+ priv->wsm_caps.hw_id,
+ priv->wsm_caps.hw_subid);
+ seq_printf(seq, "Firmware: %s %d.%d\n",
+ cw1200_fw_types[priv->wsm_caps.fw_type],
+ priv->wsm_caps.fw_ver,
+ priv->wsm_caps.fw_build);
+ seq_printf(seq, "FW API: %d\n",
+ priv->wsm_caps.fw_api);
+ seq_printf(seq, "FW caps: 0x%.4X\n",
+ priv->wsm_caps.fw_cap);
+ seq_printf(seq, "FW label: '%s'\n",
+ priv->wsm_caps.fw_label);
+ seq_printf(seq, "Mode: %s%s\n",
+ cw1200_debug_mode(priv->mode),
+ priv->listening ? " (listening)" : "");
+ seq_printf(seq, "Join state: %s\n",
+ cw1200_debug_join_status[priv->join_status]);
+ if (priv->channel)
+ seq_printf(seq, "Channel: %d%s\n",
+ priv->channel->hw_value,
+ priv->channel_switch_in_progress ?
+ " (switching)" : "");
+ if (priv->rx_filter.promiscuous)
+ seq_puts(seq, "Filter: promisc\n");
+ else if (priv->rx_filter.fcs)
+ seq_puts(seq, "Filter: fcs\n");
+ if (priv->rx_filter.bssid)
+ seq_puts(seq, "Filter: bssid\n");
+ if (!priv->disable_beacon_filter)
+ seq_puts(seq, "Filter: beacons\n");
+
+ if (priv->enable_beacon ||
+ priv->mode == NL80211_IFTYPE_AP ||
+ priv->mode == NL80211_IFTYPE_ADHOC ||
+ priv->mode == NL80211_IFTYPE_MESH_POINT ||
+ priv->mode == NL80211_IFTYPE_P2P_GO)
+ seq_printf(seq, "Beaconing: %s\n",
+ priv->enable_beacon ?
+ "enabled" : "disabled");
+
+ for (i = 0; i < 4; ++i)
+ seq_printf(seq, "EDCA(%d): %d, %d, %d, %d, %d\n", i,
+ priv->edca.params[i].cwmin,
+ priv->edca.params[i].cwmax,
+ priv->edca.params[i].aifns,
+ priv->edca.params[i].txop_limit,
+ priv->edca.params[i].max_rx_lifetime);
+
+ if (priv->join_status == CW1200_JOIN_STATUS_STA) {
+ static const char *pm_mode = "unknown";
+ switch (priv->powersave_mode.mode) {
+ case WSM_PSM_ACTIVE:
+ pm_mode = "off";
+ break;
+ case WSM_PSM_PS:
+ pm_mode = "on";
+ break;
+ case WSM_PSM_FAST_PS:
+ pm_mode = "dynamic";
+ break;
+ }
+ seq_printf(seq, "Preamble: %s\n",
+ cw1200_debug_preamble[priv->association_mode.preamble]);
+ seq_printf(seq, "AMPDU spcn: %d\n",
+ priv->association_mode.mpdu_start_spacing);
+ seq_printf(seq, "Basic rate: 0x%.8X\n",
+ le32_to_cpu(priv->association_mode.basic_rate_set));
+ seq_printf(seq, "Bss lost: %d beacons\n",
+ priv->bss_params.beacon_lost_count);
+ seq_printf(seq, "AID: %d\n",
+ priv->bss_params.aid);
+ seq_printf(seq, "Rates: 0x%.8X\n",
+ priv->bss_params.operational_rate_set);
+ seq_printf(seq, "Powersave: %s\n", pm_mode);
+ }
+ seq_printf(seq, "HT: %s\n",
+ cw1200_is_ht(&priv->ht_info) ? "on" : "off");
+ if (cw1200_is_ht(&priv->ht_info)) {
+ seq_printf(seq, "Greenfield: %s\n",
+ cw1200_ht_greenfield(&priv->ht_info) ? "yes" : "no");
+ seq_printf(seq, "AMPDU dens: %d\n",
+ cw1200_ht_ampdu_density(&priv->ht_info));
+ }
+ seq_printf(seq, "RSSI thold: %d\n",
+ priv->cqm_rssi_thold);
+ seq_printf(seq, "RSSI hyst: %d\n",
+ priv->cqm_rssi_hyst);
+ seq_printf(seq, "Long retr: %d\n",
+ priv->long_frame_max_tx_count);
+ seq_printf(seq, "Short retr: %d\n",
+ priv->short_frame_max_tx_count);
+ spin_lock_bh(&priv->tx_policy_cache.lock);
+ i = 0;
+ list_for_each(item, &priv->tx_policy_cache.used)
+ ++i;
+ spin_unlock_bh(&priv->tx_policy_cache.lock);
+ seq_printf(seq, "RC in use: %d\n", i);
+
+ seq_puts(seq, "\n");
+ for (i = 0; i < 4; ++i) {
+ cw1200_queue_status_show(seq, &priv->tx_queue[i]);
+ seq_puts(seq, "\n");
+ }
+
+ cw1200_debug_print_map(seq, priv, "Link map: ",
+ priv->link_id_map);
+ cw1200_debug_print_map(seq, priv, "Asleep map: ",
+ priv->sta_asleep_mask);
+ cw1200_debug_print_map(seq, priv, "PSPOLL map: ",
+ priv->pspoll_mask);
+
+ seq_puts(seq, "\n");
+
+ for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
+ if (priv->link_id_db[i].status) {
+ seq_printf(seq, "Link %d: %s, %pM\n",
+ i + 1,
+ cw1200_debug_link_id[priv->link_id_db[i].status],
+ priv->link_id_db[i].mac);
+ }
+ }
+
+ seq_puts(seq, "\n");
+
+ seq_printf(seq, "BH status: %s\n",
+ atomic_read(&priv->bh_term) ? "terminated" : "alive");
+ seq_printf(seq, "Pending RX: %d\n",
+ atomic_read(&priv->bh_rx));
+ seq_printf(seq, "Pending TX: %d\n",
+ atomic_read(&priv->bh_tx));
+ if (priv->bh_error)
+ seq_printf(seq, "BH errcode: %d\n",
+ priv->bh_error);
+ seq_printf(seq, "TX bufs: %d x %d bytes\n",
+ priv->wsm_caps.input_buffers,
+ priv->wsm_caps.input_buffer_size);
+ seq_printf(seq, "Used bufs: %d\n",
+ priv->hw_bufs_used);
+ seq_printf(seq, "Powermgmt: %s\n",
+ priv->powersave_enabled ? "on" : "off");
+ seq_printf(seq, "Device: %s\n",
+ priv->device_can_sleep ? "asleep" : "awake");
+
+ spin_lock(&priv->wsm_cmd.lock);
+ seq_printf(seq, "WSM status: %s\n",
+ priv->wsm_cmd.done ? "idle" : "active");
+ seq_printf(seq, "WSM cmd: 0x%.4X (%td bytes)\n",
+ priv->wsm_cmd.cmd, priv->wsm_cmd.len);
+ seq_printf(seq, "WSM retval: %d\n",
+ priv->wsm_cmd.ret);
+ spin_unlock(&priv->wsm_cmd.lock);
+
+ seq_printf(seq, "Datapath: %s\n",
+ atomic_read(&priv->tx_lock) ? "locked" : "unlocked");
+ if (atomic_read(&priv->tx_lock))
+ seq_printf(seq, "TXlock cnt: %d\n",
+ atomic_read(&priv->tx_lock));
+
+ seq_printf(seq, "TXed: %d\n",
+ d->tx);
+ seq_printf(seq, "AGG TXed: %d\n",
+ d->tx_agg);
+ seq_printf(seq, "MULTI TXed: %d (%d)\n",
+ d->tx_multi, d->tx_multi_frames);
+ seq_printf(seq, "RXed: %d\n",
+ d->rx);
+ seq_printf(seq, "AGG RXed: %d\n",
+ d->rx_agg);
+ seq_printf(seq, "TX miss: %d\n",
+ d->tx_cache_miss);
+ seq_printf(seq, "TX align: %d\n",
+ d->tx_align);
+ seq_printf(seq, "TX burst: %d\n",
+ d->tx_burst);
+ seq_printf(seq, "TX TTL: %d\n",
+ d->tx_ttl);
+ seq_printf(seq, "Scan: %s\n",
+ atomic_read(&priv->scan.in_progress) ? "active" : "idle");
+
+ return 0;
+}
+
+static int cw1200_status_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, &cw1200_status_show,
+ inode->i_private);
+}
+
+static const struct file_operations fops_status = {
+ .open = cw1200_status_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int cw1200_counters_show(struct seq_file *seq, void *v)
+{
+ int ret;
+ struct cw1200_common *priv = seq->private;
+ struct wsm_mib_counters_table counters;
+
+ ret = wsm_get_counters_table(priv, &counters);
+ if (ret)
+ return ret;
+
+#define PUT_COUNTER(tab, name) \
+ seq_printf(seq, "%s:" tab "%d\n", #name, \
+ __le32_to_cpu(counters.name))
+
+ PUT_COUNTER("\t\t", plcp_errors);
+ PUT_COUNTER("\t\t", fcs_errors);
+ PUT_COUNTER("\t\t", tx_packets);
+ PUT_COUNTER("\t\t", rx_packets);
+ PUT_COUNTER("\t\t", rx_packet_errors);
+ PUT_COUNTER("\t", rx_decryption_failures);
+ PUT_COUNTER("\t\t", rx_mic_failures);
+ PUT_COUNTER("\t", rx_no_key_failures);
+ PUT_COUNTER("\t", tx_multicast_frames);
+ PUT_COUNTER("\t", tx_frames_success);
+ PUT_COUNTER("\t", tx_frame_failures);
+ PUT_COUNTER("\t", tx_frames_retried);
+ PUT_COUNTER("\t", tx_frames_multi_retried);
+ PUT_COUNTER("\t", rx_frame_duplicates);
+ PUT_COUNTER("\t\t", rts_success);
+ PUT_COUNTER("\t\t", rts_failures);
+ PUT_COUNTER("\t\t", ack_failures);
+ PUT_COUNTER("\t", rx_multicast_frames);
+ PUT_COUNTER("\t", rx_frames_success);
+ PUT_COUNTER("\t", rx_cmac_icv_errors);
+ PUT_COUNTER("\t\t", rx_cmac_replays);
+ PUT_COUNTER("\t", rx_mgmt_ccmp_replays);
+
+#undef PUT_COUNTER
+
+ return 0;
+}
+
+static int cw1200_counters_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, &cw1200_counters_show,
+ inode->i_private);
+}
+
+static const struct file_operations fops_counters = {
+ .open = cw1200_counters_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static ssize_t cw1200_wsm_dumps(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct cw1200_common *priv = file->private_data;
+ char buf[1];
+
+ if (!count)
+ return -EINVAL;
+ if (copy_from_user(buf, user_buf, 1))
+ return -EFAULT;
+
+ if (buf[0] == '1')
+ priv->wsm_enable_wsm_dumps = 1;
+ else
+ priv->wsm_enable_wsm_dumps = 0;
+
+ return count;
+}
+
+static const struct file_operations fops_wsm_dumps = {
+ .open = simple_open,
+ .write = cw1200_wsm_dumps,
+ .llseek = default_llseek,
+};
+
+int cw1200_debug_init(struct cw1200_common *priv)
+{
+ int ret = -ENOMEM;
+ struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv),
+ GFP_KERNEL);
+ priv->debug = d;
+ if (!d)
+ return ret;
+
+ d->debugfs_phy = debugfs_create_dir("cw1200",
+ priv->hw->wiphy->debugfsdir);
+ if (!d->debugfs_phy)
+ goto err;
+
+ if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy,
+ priv, &fops_status))
+ goto err;
+
+ if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy,
+ priv, &fops_counters))
+ goto err;
+
+ if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy,
+ priv, &fops_wsm_dumps))
+ goto err;
+
+ return 0;
+
+err:
+ priv->debug = NULL;
+ debugfs_remove_recursive(d->debugfs_phy);
+ kfree(d);
+ return ret;
+}
+
+void cw1200_debug_release(struct cw1200_common *priv)
+{
+ struct cw1200_debug_priv *d = priv->debug;
+ if (d) {
+ debugfs_remove_recursive(d->debugfs_phy);
+ priv->debug = NULL;
+ kfree(d);
+ }
+}
diff --git a/drivers/net/wireless/cw1200/debug.h b/drivers/net/wireless/cw1200/debug.h
new file mode 100644
index 000000000000..b525aba53bfc
--- /dev/null
+++ b/drivers/net/wireless/cw1200/debug.h
@@ -0,0 +1,93 @@
+/*
+ * DebugFS code for ST-Ericsson CW1200 mac80211 driver
+ *
+ * Copyright (c) 2011, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 CW1200_DEBUG_H_INCLUDED
+#define CW1200_DEBUG_H_INCLUDED
+
+struct cw1200_debug_priv {
+ struct dentry *debugfs_phy;
+ int tx;
+ int tx_agg;
+ int rx;
+ int rx_agg;
+ int tx_multi;
+ int tx_multi_frames;
+ int tx_cache_miss;
+ int tx_align;
+ int tx_ttl;
+ int tx_burst;
+ int ba_cnt;
+ int ba_acc;
+ int ba_cnt_rx;
+ int ba_acc_rx;
+};
+
+int cw1200_debug_init(struct cw1200_common *priv);
+void cw1200_debug_release(struct cw1200_common *priv);
+
+static inline void cw1200_debug_txed(struct cw1200_common *priv)
+{
+ ++priv->debug->tx;
+}
+
+static inline void cw1200_debug_txed_agg(struct cw1200_common *priv)
+{
+ ++priv->debug->tx_agg;
+}
+
+static inline void cw1200_debug_txed_multi(struct cw1200_common *priv,
+ int count)
+{
+ ++priv->debug->tx_multi;
+ priv->debug->tx_multi_frames += count;
+}
+
+static inline void cw1200_debug_rxed(struct cw1200_common *priv)
+{
+ ++priv->debug->rx;
+}
+
+static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv)
+{
+ ++priv->debug->rx_agg;
+}
+
+static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv)
+{
+ ++priv->debug->tx_cache_miss;
+}
+
+static inline void cw1200_debug_tx_align(struct cw1200_common *priv)
+{
+ ++priv->debug->tx_align;
+}
+
+static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv)
+{
+ ++priv->debug->tx_ttl;
+}
+
+static inline void cw1200_debug_tx_burst(struct cw1200_common *priv)
+{
+ ++priv->debug->tx_burst;
+}
+
+static inline void cw1200_debug_ba(struct cw1200_common *priv,
+ int ba_cnt, int ba_acc,
+ int ba_cnt_rx, int ba_acc_rx)
+{
+ priv->debug->ba_cnt = ba_cnt;
+ priv->debug->ba_acc = ba_acc;
+ priv->debug->ba_cnt_rx = ba_cnt_rx;
+ priv->debug->ba_acc_rx = ba_acc_rx;
+}
+
+#endif /* CW1200_DEBUG_H_INCLUDED */
diff --git a/drivers/net/wireless/cw1200/fwio.c b/drivers/net/wireless/cw1200/fwio.c
new file mode 100644
index 000000000000..acdff0f7f952
--- /dev/null
+++ b/drivers/net/wireless/cw1200/fwio.c
@@ -0,0 +1,520 @@
+/*
+ * Firmware I/O code for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@stericsson.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 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+
+#include "cw1200.h"
+#include "fwio.h"
+#include "hwio.h"
+#include "hwbus.h"
+#include "bh.h"
+
+static int cw1200_get_hw_type(u32 config_reg_val, int *major_revision)
+{
+ int hw_type = -1;
+ u32 silicon_type = (config_reg_val >> 24) & 0x7;
+ u32 silicon_vers = (config_reg_val >> 31) & 0x1;
+
+ switch (silicon_type) {
+ case 0x00:
+ *major_revision = 1;
+ hw_type = HIF_9000_SILICON_VERSATILE;
+ break;
+ case 0x01:
+ case 0x02: /* CW1x00 */
+ case 0x04: /* CW1x60 */
+ *major_revision = silicon_type;
+ if (silicon_vers)
+ hw_type = HIF_8601_VERSATILE;
+ else
+ hw_type = HIF_8601_SILICON;
+ break;
+ default:
+ break;
+ }
+
+ return hw_type;
+}
+
+static int cw1200_load_firmware_cw1200(struct cw1200_common *priv)
+{
+ int ret, block, num_blocks;
+ unsigned i;
+ u32 val32;
+ u32 put = 0, get = 0;
+ u8 *buf = NULL;
+ const char *fw_path;
+ const struct firmware *firmware = NULL;
+
+ /* Macroses are local. */
+#define APB_WRITE(reg, val) \
+ do { \
+ ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \
+ if (ret < 0) \
+ goto error; \
+ } while (0)
+#define APB_READ(reg, val) \
+ do { \
+ ret = cw1200_apb_read_32(priv, CW1200_APB(reg), &(val)); \
+ if (ret < 0) \
+ goto error; \
+ } while (0)
+#define REG_WRITE(reg, val) \
+ do { \
+ ret = cw1200_reg_write_32(priv, (reg), (val)); \
+ if (ret < 0) \
+ goto error; \
+ } while (0)
+#define REG_READ(reg, val) \
+ do { \
+ ret = cw1200_reg_read_32(priv, (reg), &(val)); \
+ if (ret < 0) \
+ goto error; \
+ } while (0)
+
+ switch (priv->hw_revision) {
+ case CW1200_HW_REV_CUT10:
+ fw_path = FIRMWARE_CUT10;
+ if (!priv->sdd_path)
+ priv->sdd_path = SDD_FILE_10;
+ break;
+ case CW1200_HW_REV_CUT11:
+ fw_path = FIRMWARE_CUT11;
+ if (!priv->sdd_path)
+ priv->sdd_path = SDD_FILE_11;
+ break;
+ case CW1200_HW_REV_CUT20:
+ fw_path = FIRMWARE_CUT20;
+ if (!priv->sdd_path)
+ priv->sdd_path = SDD_FILE_20;
+ break;
+ case CW1200_HW_REV_CUT22:
+ fw_path = FIRMWARE_CUT22;
+ if (!priv->sdd_path)
+ priv->sdd_path = SDD_FILE_22;
+ break;
+ case CW1X60_HW_REV:
+ fw_path = FIRMWARE_CW1X60;
+ if (!priv->sdd_path)
+ priv->sdd_path = SDD_FILE_CW1X60;
+ break;
+ default:
+ pr_err("Invalid silicon revision %d.\n", priv->hw_revision);
+ return -EINVAL;
+ }
+
+ /* Initialize common registers */
+ APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE);
+ APB_WRITE(DOWNLOAD_PUT_REG, 0);
+ APB_WRITE(DOWNLOAD_GET_REG, 0);
+ APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING);
+ APB_WRITE(DOWNLOAD_FLAGS_REG, 0);
+
+ /* Write the NOP Instruction */
+ REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000);
+ REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE);
+
+ /* Release CPU from RESET */
+ REG_READ(ST90TDS_CONFIG_REG_ID, val32);
+ val32 &= ~ST90TDS_CONFIG_CPU_RESET_BIT;
+ REG_WRITE(ST90TDS_CONFIG_REG_ID, val32);
+
+ /* Enable Clock */
+ val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT;
+ REG_WRITE(ST90TDS_CONFIG_REG_ID, val32);
+
+ /* Load a firmware file */
+ ret = request_firmware(&firmware, fw_path, priv->pdev);
+ if (ret) {
+ pr_err("Can't load firmware file %s.\n", fw_path);
+ goto error;
+ }
+
+ buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);
+ if (!buf) {
+ pr_err("Can't allocate firmware load buffer.\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /* Check if the bootloader is ready */
+ for (i = 0; i < 100; i += 1 + i / 2) {
+ APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32);
+ if (val32 == DOWNLOAD_I_AM_HERE)
+ break;
+ mdelay(i);
+ } /* End of for loop */
+
+ if (val32 != DOWNLOAD_I_AM_HERE) {
+ pr_err("Bootloader is not ready.\n");
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ /* Calculcate number of download blocks */
+ num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1;
+
+ /* Updating the length in Download Ctrl Area */
+ val32 = firmware->size; /* Explicit cast from size_t to u32 */
+ APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, val32);
+
+ /* Firmware downloading loop */
+ for (block = 0; block < num_blocks; block++) {
+ size_t tx_size;
+ size_t block_size;
+
+ /* check the download status */
+ APB_READ(DOWNLOAD_STATUS_REG, val32);
+ if (val32 != DOWNLOAD_PENDING) {
+ pr_err("Bootloader reported error %d.\n", val32);
+ ret = -EIO;
+ goto error;
+ }
+
+ /* loop until put - get <= 24K */
+ for (i = 0; i < 100; i++) {
+ APB_READ(DOWNLOAD_GET_REG, get);
+ if ((put - get) <=
+ (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE))
+ break;
+ mdelay(i);
+ }
+
+ if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) {
+ pr_err("Timeout waiting for FIFO.\n");
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ /* calculate the block size */
+ tx_size = block_size = min((size_t)(firmware->size - put),
+ (size_t)DOWNLOAD_BLOCK_SIZE);
+
+ memcpy(buf, &firmware->data[put], block_size);
+ if (block_size < DOWNLOAD_BLOCK_SIZE) {
+ memset(&buf[block_size], 0,
+ DOWNLOAD_BLOCK_SIZE - block_size);
+ tx_size = DOWNLOAD_BLOCK_SIZE;
+ }
+
+ /* send the block to sram */
+ ret = cw1200_apb_write(priv,
+ CW1200_APB(DOWNLOAD_FIFO_OFFSET +
+ (put & (DOWNLOAD_FIFO_SIZE - 1))),
+ buf, tx_size);
+ if (ret < 0) {
+ pr_err("Can't write firmware block @ %d!\n",
+ put & (DOWNLOAD_FIFO_SIZE - 1));
+ goto error;
+ }
+
+ /* update the put register */
+ put += block_size;
+ APB_WRITE(DOWNLOAD_PUT_REG, put);
+ } /* End of firmware download loop */
+
+ /* Wait for the download completion */
+ for (i = 0; i < 300; i += 1 + i / 2) {
+ APB_READ(DOWNLOAD_STATUS_REG, val32);
+ if (val32 != DOWNLOAD_PENDING)
+ break;
+ mdelay(i);
+ }
+ if (val32 != DOWNLOAD_SUCCESS) {
+ pr_err("Wait for download completion failed: 0x%.8X\n", val32);
+ ret = -ETIMEDOUT;
+ goto error;
+ } else {
+ pr_info("Firmware download completed.\n");
+ ret = 0;
+ }
+
+error:
+ kfree(buf);
+ if (firmware)
+ release_firmware(firmware);
+ return ret;
+
+#undef APB_WRITE
+#undef APB_READ
+#undef REG_WRITE
+#undef REG_READ
+}
+
+
+static int config_reg_read(struct cw1200_common *priv, u32 *val)
+{
+ switch (priv->hw_type) {
+ case HIF_9000_SILICON_VERSATILE: {
+ u16 val16;
+ int ret = cw1200_reg_read_16(priv,
+ ST90TDS_CONFIG_REG_ID,
+ &val16);
+ if (ret < 0)
+ return ret;
+ *val = val16;
+ return 0;
+ }
+ case HIF_8601_VERSATILE:
+ case HIF_8601_SILICON:
+ default:
+ cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, val);
+ break;
+ }
+ return 0;
+}
+
+static int config_reg_write(struct cw1200_common *priv, u32 val)
+{
+ switch (priv->hw_type) {
+ case HIF_9000_SILICON_VERSATILE:
+ return cw1200_reg_write_16(priv,
+ ST90TDS_CONFIG_REG_ID,
+ (u16)val);
+ case HIF_8601_VERSATILE:
+ case HIF_8601_SILICON:
+ default:
+ return cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val);
+ break;
+ }
+ return 0;
+}
+
+int cw1200_load_firmware(struct cw1200_common *priv)
+{
+ int ret;
+ int i;
+ u32 val32;
+ u16 val16;
+ int major_revision = -1;
+
+ /* Read CONFIG Register */
+ ret = cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ pr_err("Can't read config register.\n");
+ goto out;
+ }
+
+ if (val32 == 0 || val32 == 0xffffffff) {
+ pr_err("Bad config register value (0x%08x)\n", val32);
+ ret = -EIO;
+ goto out;
+ }
+
+ priv->hw_type = cw1200_get_hw_type(val32, &major_revision);
+ if (priv->hw_type < 0) {
+ pr_err("Can't deduce hardware type.\n");
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ /* Set DPLL Reg value, and read back to confirm writes work */
+ ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID,
+ cw1200_dpll_from_clk(priv->hw_refclk));
+ if (ret < 0) {
+ pr_err("Can't write DPLL register.\n");
+ goto out;
+ }
+
+ msleep(20);
+
+ ret = cw1200_reg_read_32(priv,
+ ST90TDS_TSET_GEN_R_W_REG_ID, &val32);
+ if (ret < 0) {
+ pr_err("Can't read DPLL register.\n");
+ goto out;
+ }
+
+ if (val32 != cw1200_dpll_from_clk(priv->hw_refclk)) {
+ pr_err("Unable to initialise DPLL register. Wrote 0x%.8X, Read 0x%.8X.\n",
+ cw1200_dpll_from_clk(priv->hw_refclk), val32);
+ ret = -EIO;
+ goto out;
+ }
+
+ /* Set wakeup bit in device */
+ ret = cw1200_reg_read_16(priv, ST90TDS_CONTROL_REG_ID, &val16);
+ if (ret < 0) {
+ pr_err("set_wakeup: can't read control register.\n");
+ goto out;
+ }
+
+ ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID,
+ val16 | ST90TDS_CONT_WUP_BIT);
+ if (ret < 0) {
+ pr_err("set_wakeup: can't write control register.\n");
+ goto out;
+ }
+
+ /* Wait for wakeup */
+ for (i = 0; i < 300; i += (1 + i / 2)) {
+ ret = cw1200_reg_read_16(priv,
+ ST90TDS_CONTROL_REG_ID, &val16);
+ if (ret < 0) {
+ pr_err("wait_for_wakeup: can't read control register.\n");
+ goto out;
+ }
+
+ if (val16 & ST90TDS_CONT_RDY_BIT)
+ break;
+
+ msleep(i);
+ }
+
+ if ((val16 & ST90TDS_CONT_RDY_BIT) == 0) {
+ pr_err("wait_for_wakeup: device is not responding.\n");
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ switch (major_revision) {
+ case 1:
+ /* CW1200 Hardware detection logic : Check for CUT1.1 */
+ ret = cw1200_ahb_read_32(priv, CW1200_CUT_ID_ADDR, &val32);
+ if (ret) {
+ pr_err("HW detection: can't read CUT ID.\n");
+ goto out;
+ }
+
+ switch (val32) {
+ case CW1200_CUT_11_ID_STR:
+ pr_info("CW1x00 Cut 1.1 silicon detected.\n");
+ priv->hw_revision = CW1200_HW_REV_CUT11;
+ break;
+ default:
+ pr_info("CW1x00 Cut 1.0 silicon detected.\n");
+ priv->hw_revision = CW1200_HW_REV_CUT10;
+ break;
+ }
+
+ /* According to ST-E, CUT<2.0 has busted BA TID0-3.
+ Just disable it entirely...
+ */
+ priv->ba_rx_tid_mask = 0;
+ priv->ba_tx_tid_mask = 0;
+ break;
+ case 2: {
+ u32 ar1, ar2, ar3;
+ ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR, &ar1);
+ if (ret) {
+ pr_err("(1) HW detection: can't read CUT ID\n");
+ goto out;
+ }
+ ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 4, &ar2);
+ if (ret) {
+ pr_err("(2) HW detection: can't read CUT ID.\n");
+ goto out;
+ }
+
+ ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 8, &ar3);
+ if (ret) {
+ pr_err("(3) HW detection: can't read CUT ID.\n");
+ goto out;
+ }
+
+ if (ar1 == CW1200_CUT_22_ID_STR1 &&
+ ar2 == CW1200_CUT_22_ID_STR2 &&
+ ar3 == CW1200_CUT_22_ID_STR3) {
+ pr_info("CW1x00 Cut 2.2 silicon detected.\n");
+ priv->hw_revision = CW1200_HW_REV_CUT22;
+ } else {
+ pr_info("CW1x00 Cut 2.0 silicon detected.\n");
+ priv->hw_revision = CW1200_HW_REV_CUT20;
+ }
+ break;
+ }
+ case 4:
+ pr_info("CW1x60 silicon detected.\n");
+ priv->hw_revision = CW1X60_HW_REV;
+ break;
+ default:
+ pr_err("Unsupported silicon major revision %d.\n",
+ major_revision);
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ /* Checking for access mode */
+ ret = config_reg_read(priv, &val32);
+ if (ret < 0) {
+ pr_err("Can't read config register.\n");
+ goto out;
+ }
+
+ if (!(val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT)) {
+ pr_err("Device is already in QUEUE mode!\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ switch (priv->hw_type) {
+ case HIF_8601_SILICON:
+ if (priv->hw_revision == CW1X60_HW_REV) {
+ pr_err("Can't handle CW1160/1260 firmware load yet.\n");
+ ret = -ENOTSUPP;
+ goto out;
+ }
+ ret = cw1200_load_firmware_cw1200(priv);
+ break;
+ default:
+ pr_err("Can't perform firmware load for hw type %d.\n",
+ priv->hw_type);
+ ret = -ENOTSUPP;
+ goto out;
+ }
+ if (ret < 0) {
+ pr_err("Firmware load error.\n");
+ goto out;
+ }
+
+ /* Enable interrupt signalling */
+ priv->hwbus_ops->lock(priv->hwbus_priv);
+ ret = __cw1200_irq_enable(priv, 1);
+ priv->hwbus_ops->unlock(priv->hwbus_priv);
+ if (ret < 0)
+ goto unsubscribe;
+
+ /* Configure device for MESSSAGE MODE */
+ ret = config_reg_read(priv, &val32);
+ if (ret < 0) {
+ pr_err("Can't read config register.\n");
+ goto unsubscribe;
+ }
+ ret = config_reg_write(priv, val32 & ~ST90TDS_CONFIG_ACCESS_MODE_BIT);
+ if (ret < 0) {
+ pr_err("Can't write config register.\n");
+ goto unsubscribe;
+ }
+
+ /* Unless we read the CONFIG Register we are
+ * not able to get an interrupt
+ */
+ mdelay(10);
+ config_reg_read(priv, &val32);
+
+out:
+ return ret;
+
+unsubscribe:
+ /* Disable interrupt signalling */
+ priv->hwbus_ops->lock(priv->hwbus_priv);
+ ret = __cw1200_irq_enable(priv, 0);
+ priv->hwbus_ops->unlock(priv->hwbus_priv);
+ return ret;
+}
diff --git a/drivers/net/wireless/cw1200/fwio.h b/drivers/net/wireless/cw1200/fwio.h
new file mode 100644
index 000000000000..ea3099362cdf
--- /dev/null
+++ b/drivers/net/wireless/cw1200/fwio.h
@@ -0,0 +1,39 @@
+/*
+ * Firmware API for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@stericsson.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 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef FWIO_H_INCLUDED
+#define FWIO_H_INCLUDED
+
+#define BOOTLOADER_CW1X60 "boot_cw1x60.bin"
+#define FIRMWARE_CW1X60 "wsm_cw1x60.bin"
+#define FIRMWARE_CUT22 "wsm_22.bin"
+#define FIRMWARE_CUT20 "wsm_20.bin"
+#define FIRMWARE_CUT11 "wsm_11.bin"
+#define FIRMWARE_CUT10 "wsm_10.bin"
+#define SDD_FILE_CW1X60 "sdd_cw1x60.bin"
+#define SDD_FILE_22 "sdd_22.bin"
+#define SDD_FILE_20 "sdd_20.bin"
+#define SDD_FILE_11 "sdd_11.bin"
+#define SDD_FILE_10 "sdd_10.bin"
+
+int cw1200_load_firmware(struct cw1200_common *priv);
+
+/* SDD definitions */
+#define SDD_PTA_CFG_ELT_ID 0xEB
+#define SDD_REFERENCE_FREQUENCY_ELT_ID 0xc5
+u32 cw1200_dpll_from_clk(u16 clk);
+
+#endif
diff --git a/drivers/net/wireless/cw1200/hwbus.h b/drivers/net/wireless/cw1200/hwbus.h
new file mode 100644
index 000000000000..8b2fc831c3de
--- /dev/null
+++ b/drivers/net/wireless/cw1200/hwbus.h
@@ -0,0 +1,33 @@
+/*
+ * Common hwbus abstraction layer interface for cw1200 wireless driver
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 CW1200_HWBUS_H
+#define CW1200_HWBUS_H
+
+struct hwbus_priv;
+
+void cw1200_irq_handler(struct cw1200_common *priv);
+
+/* This MUST be wrapped with hwbus_ops->lock/unlock! */
+int __cw1200_irq_enable(struct cw1200_common *priv, int enable);
+
+struct hwbus_ops {
+ int (*hwbus_memcpy_fromio)(struct hwbus_priv *self, unsigned int addr,
+ void *dst, int count);
+ int (*hwbus_memcpy_toio)(struct hwbus_priv *self, unsigned int addr,
+ const void *src, int count);
+ void (*lock)(struct hwbus_priv *self);
+ void (*unlock)(struct hwbus_priv *self);
+ size_t (*align_size)(struct hwbus_priv *self, size_t size);
+ int (*power_mgmt)(struct hwbus_priv *self, bool suspend);
+};
+
+#endif /* CW1200_HWBUS_H */
diff --git a/drivers/net/wireless/cw1200/hwio.c b/drivers/net/wireless/cw1200/hwio.c
new file mode 100644
index 000000000000..dad3fb331818
--- /dev/null
+++ b/drivers/net/wireless/cw1200/hwio.c
@@ -0,0 +1,310 @@
+/*
+ * Low-level device IO routines for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver, which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@lockless.no>
+ *
+ * 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/types.h>
+
+#include "cw1200.h"
+#include "hwio.h"
+#include "hwbus.h"
+
+ /* Sdio addr is 4*spi_addr */
+#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2)
+#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \
+ ((((buf_id) & 0x1F) << 7) \
+ | (((mpf) & 1) << 6) \
+ | (((rfu) & 1) << 5) \
+ | (((reg_id_ofs) & 0x1F) << 0))
+#define MAX_RETRY 3
+
+
+static int __cw1200_reg_read(struct cw1200_common *priv, u16 addr,
+ void *buf, size_t buf_len, int buf_id)
+{
+ u16 addr_sdio;
+ u32 sdio_reg_addr_17bit;
+
+ /* Check if buffer is aligned to 4 byte boundary */
+ if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) {
+ pr_err("buffer is not aligned.\n");
+ return -EINVAL;
+ }
+
+ /* Convert to SDIO Register Address */
+ addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
+ sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
+
+ return priv->hwbus_ops->hwbus_memcpy_fromio(priv->hwbus_priv,
+ sdio_reg_addr_17bit,
+ buf, buf_len);
+}
+
+static int __cw1200_reg_write(struct cw1200_common *priv, u16 addr,
+ const void *buf, size_t buf_len, int buf_id)
+{
+ u16 addr_sdio;
+ u32 sdio_reg_addr_17bit;
+
+ /* Convert to SDIO Register Address */
+ addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
+ sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
+
+ return priv->hwbus_ops->hwbus_memcpy_toio(priv->hwbus_priv,
+ sdio_reg_addr_17bit,
+ buf, buf_len);
+}
+
+static inline int __cw1200_reg_read_32(struct cw1200_common *priv,
+ u16 addr, u32 *val)
+{
+ int i = __cw1200_reg_read(priv, addr, val, sizeof(*val), 0);
+ *val = le32_to_cpu(*val);
+ return i;
+}
+
+static inline int __cw1200_reg_write_32(struct cw1200_common *priv,
+ u16 addr, u32 val)
+{
+ val = cpu_to_le32(val);
+ return __cw1200_reg_write(priv, addr, &val, sizeof(val), 0);
+}
+
+static inline int __cw1200_reg_read_16(struct cw1200_common *priv,
+ u16 addr, u16 *val)
+{
+ int i = __cw1200_reg_read(priv, addr, val, sizeof(*val), 0);
+ *val = le16_to_cpu(*val);
+ return i;
+}
+
+static inline int __cw1200_reg_write_16(struct cw1200_common *priv,
+ u16 addr, u16 val)
+{
+ val = cpu_to_le16(val);
+ return __cw1200_reg_write(priv, addr, &val, sizeof(val), 0);
+}
+
+int cw1200_reg_read(struct cw1200_common *priv, u16 addr, void *buf,
+ size_t buf_len)
+{
+ int ret;
+ priv->hwbus_ops->lock(priv->hwbus_priv);
+ ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0);
+ priv->hwbus_ops->unlock(priv->hwbus_priv);
+ return ret;
+}
+
+int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf,
+ size_t buf_len)
+{
+ int ret;
+ priv->hwbus_ops->lock(priv->hwbus_priv);
+ ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0);
+ priv->hwbus_ops->unlock(priv->hwbus_priv);
+ return ret;
+}
+
+int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len)
+{
+ int ret, retry = 1;
+ int buf_id_rx = priv->buf_id_rx;
+
+ priv->hwbus_ops->lock(priv->hwbus_priv);
+
+ while (retry <= MAX_RETRY) {
+ ret = __cw1200_reg_read(priv,
+ ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
+ buf_len, buf_id_rx + 1);
+ if (!ret) {
+ buf_id_rx = (buf_id_rx + 1) & 3;
+ priv->buf_id_rx = buf_id_rx;
+ break;
+ } else {
+ retry++;
+ mdelay(1);
+ pr_err("error :[%d]\n", ret);
+ }
+ }
+
+ priv->hwbus_ops->unlock(priv->hwbus_priv);
+ return ret;
+}
+
+int cw1200_data_write(struct cw1200_common *priv, const void *buf,
+ size_t buf_len)
+{
+ int ret, retry = 1;
+ int buf_id_tx = priv->buf_id_tx;
+
+ priv->hwbus_ops->lock(priv->hwbus_priv);
+
+ while (retry <= MAX_RETRY) {
+ ret = __cw1200_reg_write(priv,
+ ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
+ buf_len, buf_id_tx);
+ if (!ret) {
+ buf_id_tx = (buf_id_tx + 1) & 31;
+ priv->buf_id_tx = buf_id_tx;
+ break;
+ } else {
+ retry++;
+ mdelay(1);
+ pr_err("error :[%d]\n", ret);
+ }
+ }
+
+ priv->hwbus_ops->unlock(priv->hwbus_priv);
+ return ret;
+}
+
+int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf,
+ size_t buf_len, u32 prefetch, u16 port_addr)
+{
+ u32 val32 = 0;
+ int i, ret;
+
+ if ((buf_len / 2) >= 0x1000) {
+ pr_err("Can't read more than 0xfff words.\n");
+ return -EINVAL;
+ }
+
+ priv->hwbus_ops->lock(priv->hwbus_priv);
+ /* Write address */
+ ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
+ if (ret < 0) {
+ pr_err("Can't write address register.\n");
+ goto out;
+ }
+
+ /* Read CONFIG Register Value - We will read 32 bits */
+ ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ pr_err("Can't read config register.\n");
+ goto out;
+ }
+
+ /* Set PREFETCH bit */
+ ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID,
+ val32 | prefetch);
+ if (ret < 0) {
+ pr_err("Can't write prefetch bit.\n");
+ goto out;
+ }
+
+ /* Check for PRE-FETCH bit to be cleared */
+ for (i = 0; i < 20; i++) {
+ ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ pr_err("Can't check prefetch bit.\n");
+ goto out;
+ }
+ if (!(val32 & prefetch))
+ break;
+
+ mdelay(i);
+ }
+
+ if (val32 & prefetch) {
+ pr_err("Prefetch bit is not cleared.\n");
+ goto out;
+ }
+
+ /* Read data port */
+ ret = __cw1200_reg_read(priv, port_addr, buf, buf_len, 0);
+ if (ret < 0) {
+ pr_err("Can't read data port.\n");
+ goto out;
+ }
+
+out:
+ priv->hwbus_ops->unlock(priv->hwbus_priv);
+ return ret;
+}
+
+int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf,
+ size_t buf_len)
+{
+ int ret;
+
+ if ((buf_len / 2) >= 0x1000) {
+ pr_err("Can't write more than 0xfff words.\n");
+ return -EINVAL;
+ }
+
+ priv->hwbus_ops->lock(priv->hwbus_priv);
+
+ /* Write address */
+ ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
+ if (ret < 0) {
+ pr_err("Can't write address register.\n");
+ goto out;
+ }
+
+ /* Write data port */
+ ret = __cw1200_reg_write(priv, ST90TDS_SRAM_DPORT_REG_ID,
+ buf, buf_len, 0);
+ if (ret < 0) {
+ pr_err("Can't write data port.\n");
+ goto out;
+ }
+
+out:
+ priv->hwbus_ops->unlock(priv->hwbus_priv);
+ return ret;
+}
+
+int __cw1200_irq_enable(struct cw1200_common *priv, int enable)
+{
+ u32 val32;
+ u16 val16;
+ int ret;
+
+ if (HIF_8601_SILICON == priv->hw_type) {
+ ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ pr_err("Can't read config register.\n");
+ return ret;
+ }
+
+ if (enable)
+ val32 |= ST90TDS_CONF_IRQ_RDY_ENABLE;
+ else
+ val32 &= ~ST90TDS_CONF_IRQ_RDY_ENABLE;
+
+ ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val32);
+ if (ret < 0) {
+ pr_err("Can't write config register.\n");
+ return ret;
+ }
+ } else {
+ ret = __cw1200_reg_read_16(priv, ST90TDS_CONFIG_REG_ID, &val16);
+ if (ret < 0) {
+ pr_err("Can't read control register.\n");
+ return ret;
+ }
+
+ if (enable)
+ val16 |= ST90TDS_CONT_IRQ_RDY_ENABLE;
+ else
+ val16 &= ~ST90TDS_CONT_IRQ_RDY_ENABLE;
+
+ ret = __cw1200_reg_write_16(priv, ST90TDS_CONFIG_REG_ID, val16);
+ if (ret < 0) {
+ pr_err("Can't write control register.\n");
+ return ret;
+ }
+ }
+ return 0;
+}
diff --git a/drivers/net/wireless/cw1200/hwio.h b/drivers/net/wireless/cw1200/hwio.h
new file mode 100644
index 000000000000..563329cfead6
--- /dev/null
+++ b/drivers/net/wireless/cw1200/hwio.h
@@ -0,0 +1,246 @@
+/*
+ * Low-level API for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@stericsson.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 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef CW1200_HWIO_H_INCLUDED
+#define CW1200_HWIO_H_INCLUDED
+
+/* extern */ struct cw1200_common;
+
+#define CW1200_CUT_11_ID_STR (0x302E3830)
+#define CW1200_CUT_22_ID_STR1 (0x302e3132)
+#define CW1200_CUT_22_ID_STR2 (0x32302e30)
+#define CW1200_CUT_22_ID_STR3 (0x3335)
+#define CW1200_CUT_ID_ADDR (0xFFF17F90)
+#define CW1200_CUT2_ID_ADDR (0xFFF1FF90)
+
+/* Download control area */
+/* boot loader start address in SRAM */
+#define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000)
+/* 32K, 0x4000 to 0xDFFF */
+#define DOWNLOAD_FIFO_OFFSET (0x00004000)
+/* 32K */
+#define DOWNLOAD_FIFO_SIZE (0x00008000)
+/* 128 bytes, 0xFF80 to 0xFFFF */
+#define DOWNLOAD_CTRL_OFFSET (0x0000FF80)
+#define DOWNLOAD_CTRL_DATA_DWORDS (32-6)
+
+struct download_cntl_t {
+ /* size of whole firmware file (including Cheksum), host init */
+ u32 image_size;
+ /* downloading flags */
+ u32 flags;
+ /* No. of bytes put into the download, init & updated by host */
+ u32 put;
+ /* last traced program counter, last ARM reg_pc */
+ u32 trace_pc;
+ /* No. of bytes read from the download, host init, device updates */
+ u32 get;
+ /* r0, boot losader status, host init to pending, device updates */
+ u32 status;
+ /* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */
+ u32 debug_data[DOWNLOAD_CTRL_DATA_DWORDS];
+};
+
+#define DOWNLOAD_IMAGE_SIZE_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, image_size))
+#define DOWNLOAD_FLAGS_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, flags))
+#define DOWNLOAD_PUT_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, put))
+#define DOWNLOAD_TRACE_PC_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, trace_pc))
+#define DOWNLOAD_GET_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, get))
+#define DOWNLOAD_STATUS_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, status))
+#define DOWNLOAD_DEBUG_DATA_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, debug_data))
+#define DOWNLOAD_DEBUG_DATA_LEN (108)
+
+#define DOWNLOAD_BLOCK_SIZE (1024)
+
+/* For boot loader detection */
+#define DOWNLOAD_ARE_YOU_HERE (0x87654321)
+#define DOWNLOAD_I_AM_HERE (0x12345678)
+
+/* Download error code */
+#define DOWNLOAD_PENDING (0xFFFFFFFF)
+#define DOWNLOAD_SUCCESS (0)
+#define DOWNLOAD_EXCEPTION (1)
+#define DOWNLOAD_ERR_MEM_1 (2)
+#define DOWNLOAD_ERR_MEM_2 (3)
+#define DOWNLOAD_ERR_SOFTWARE (4)
+#define DOWNLOAD_ERR_FILE_SIZE (5)
+#define DOWNLOAD_ERR_CHECKSUM (6)
+#define DOWNLOAD_ERR_OVERFLOW (7)
+#define DOWNLOAD_ERR_IMAGE (8)
+#define DOWNLOAD_ERR_HOST (9)
+#define DOWNLOAD_ERR_ABORT (10)
+
+
+#define SYS_BASE_ADDR_SILICON (0)
+#define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000)
+#define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON)
+
+#define CW1200_APB(addr) (PAC_SHARED_MEMORY_SILICON + (addr))
+
+/* Device register definitions */
+
+/* WBF - SPI Register Addresses */
+#define ST90TDS_ADDR_ID_BASE (0x0000)
+/* 16/32 bits */
+#define ST90TDS_CONFIG_REG_ID (0x0000)
+/* 16/32 bits */
+#define ST90TDS_CONTROL_REG_ID (0x0001)
+/* 16 bits, Q mode W/R */
+#define ST90TDS_IN_OUT_QUEUE_REG_ID (0x0002)
+/* 32 bits, AHB bus R/W */
+#define ST90TDS_AHB_DPORT_REG_ID (0x0003)
+/* 16/32 bits */
+#define ST90TDS_SRAM_BASE_ADDR_REG_ID (0x0004)
+/* 32 bits, APB bus R/W */
+#define ST90TDS_SRAM_DPORT_REG_ID (0x0005)
+/* 32 bits, t_settle/general */
+#define ST90TDS_TSET_GEN_R_W_REG_ID (0x0006)
+/* 16 bits, Q mode read, no length */
+#define ST90TDS_FRAME_OUT_REG_ID (0x0007)
+#define ST90TDS_ADDR_ID_MAX (ST90TDS_FRAME_OUT_REG_ID)
+
+/* WBF - Control register bit set */
+/* next o/p length, bit 11 to 0 */
+#define ST90TDS_CONT_NEXT_LEN_MASK (0x0FFF)
+#define ST90TDS_CONT_WUP_BIT (BIT(12))
+#define ST90TDS_CONT_RDY_BIT (BIT(13))
+#define ST90TDS_CONT_IRQ_ENABLE (BIT(14))
+#define ST90TDS_CONT_RDY_ENABLE (BIT(15))
+#define ST90TDS_CONT_IRQ_RDY_ENABLE (BIT(14)|BIT(15))
+
+/* SPI Config register bit set */
+#define ST90TDS_CONFIG_FRAME_BIT (BIT(2))
+#define ST90TDS_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4))
+#define ST90TDS_CONFIG_WORD_MODE_1 (BIT(3))
+#define ST90TDS_CONFIG_WORD_MODE_2 (BIT(4))
+#define ST90TDS_CONFIG_ERROR_0_BIT (BIT(5))
+#define ST90TDS_CONFIG_ERROR_1_BIT (BIT(6))
+#define ST90TDS_CONFIG_ERROR_2_BIT (BIT(7))
+/* TBD: Sure??? */
+#define ST90TDS_CONFIG_CSN_FRAME_BIT (BIT(7))
+#define ST90TDS_CONFIG_ERROR_3_BIT (BIT(8))
+#define ST90TDS_CONFIG_ERROR_4_BIT (BIT(9))
+/* QueueM */
+#define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10))
+/* AHB bus */
+#define ST90TDS_CONFIG_AHB_PRFETCH_BIT (BIT(11))
+#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12))
+/* APB bus */
+#define ST90TDS_CONFIG_PRFETCH_BIT (BIT(13))
+/* cpu reset */
+#define ST90TDS_CONFIG_CPU_RESET_BIT (BIT(14))
+#define ST90TDS_CONFIG_CLEAR_INT_BIT (BIT(15))
+
+/* For CW1200 the IRQ Enable and Ready Bits are in CONFIG register */
+#define ST90TDS_CONF_IRQ_ENABLE (BIT(16))
+#define ST90TDS_CONF_RDY_ENABLE (BIT(17))
+#define ST90TDS_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17))
+
+int cw1200_data_read(struct cw1200_common *priv,
+ void *buf, size_t buf_len);
+int cw1200_data_write(struct cw1200_common *priv,
+ const void *buf, size_t buf_len);
+
+int cw1200_reg_read(struct cw1200_common *priv, u16 addr,
+ void *buf, size_t buf_len);
+int cw1200_reg_write(struct cw1200_common *priv, u16 addr,
+ const void *buf, size_t buf_len);
+
+static inline int cw1200_reg_read_16(struct cw1200_common *priv,
+ u16 addr, u16 *val)
+{
+ u32 tmp;
+ int i;
+ i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp));
+ tmp = le32_to_cpu(tmp);
+ *val = tmp & 0xffff;
+ return i;
+}
+
+static inline int cw1200_reg_write_16(struct cw1200_common *priv,
+ u16 addr, u16 val)
+{
+ u32 tmp = val;
+ tmp = cpu_to_le32(tmp);
+ return cw1200_reg_write(priv, addr, &tmp, sizeof(tmp));
+}
+
+static inline int cw1200_reg_read_32(struct cw1200_common *priv,
+ u16 addr, u32 *val)
+{
+ int i = cw1200_reg_read(priv, addr, val, sizeof(*val));
+ *val = le32_to_cpu(*val);
+ return i;
+}
+
+static inline int cw1200_reg_write_32(struct cw1200_common *priv,
+ u16 addr, u32 val)
+{
+ val = cpu_to_le32(val);
+ return cw1200_reg_write(priv, addr, &val, sizeof(val));
+}
+
+int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf,
+ size_t buf_len, u32 prefetch, u16 port_addr);
+int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf,
+ size_t buf_len);
+
+static inline int cw1200_apb_read(struct cw1200_common *priv, u32 addr,
+ void *buf, size_t buf_len)
+{
+ return cw1200_indirect_read(priv, addr, buf, buf_len,
+ ST90TDS_CONFIG_PRFETCH_BIT,
+ ST90TDS_SRAM_DPORT_REG_ID);
+}
+
+static inline int cw1200_ahb_read(struct cw1200_common *priv, u32 addr,
+ void *buf, size_t buf_len)
+{
+ return cw1200_indirect_read(priv, addr, buf, buf_len,
+ ST90TDS_CONFIG_AHB_PRFETCH_BIT,
+ ST90TDS_AHB_DPORT_REG_ID);
+}
+
+static inline int cw1200_apb_read_32(struct cw1200_common *priv,
+ u32 addr, u32 *val)
+{
+ int i = cw1200_apb_read(priv, addr, val, sizeof(*val));
+ *val = le32_to_cpu(*val);
+ return i;
+}
+
+static inline int cw1200_apb_write_32(struct cw1200_common *priv,
+ u32 addr, u32 val)
+{
+ val = cpu_to_le32(val);
+ return cw1200_apb_write(priv, addr, &val, sizeof(val));
+}
+static inline int cw1200_ahb_read_32(struct cw1200_common *priv,
+ u32 addr, u32 *val)
+{
+ int i = cw1200_ahb_read(priv, addr, val, sizeof(*val));
+ *val = le32_to_cpu(*val);
+ return i;
+}
+
+#endif /* CW1200_HWIO_H_INCLUDED */
diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c
new file mode 100644
index 000000000000..9f9adb4fbfb8
--- /dev/null
+++ b/drivers/net/wireless/cw1200/main.c
@@ -0,0 +1,600 @@
+/*
+ * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * Based on:
+ * - the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ * - stlc45xx driver
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * 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/init.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/vmalloc.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <net/mac80211.h>
+
+#include "cw1200.h"
+#include "txrx.h"
+#include "hwbus.h"
+#include "fwio.h"
+#include "hwio.h"
+#include "bh.h"
+#include "sta.h"
+#include "scan.h"
+#include "debug.h"
+#include "pm.h"
+
+MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
+MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("cw1200_core");
+
+/* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */
+static u8 cw1200_mac_template[ETH_ALEN] = {0x02, 0x80, 0xe1, 0x00, 0x00, 0x00};
+module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, S_IRUGO);
+MODULE_PARM_DESC(macaddr, "Override platform_data MAC address");
+
+static char *cw1200_sdd_path;
+module_param(cw1200_sdd_path, charp, 0644);
+MODULE_PARM_DESC(cw1200_sdd_path, "Override platform_data SDD file");
+static int cw1200_refclk;
+module_param(cw1200_refclk, int, 0644);
+MODULE_PARM_DESC(cw1200_refclk, "Override platform_data reference clock");
+
+int cw1200_power_mode = wsm_power_mode_quiescent;
+module_param(cw1200_power_mode, int, 0644);
+MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode. 0 == active, 1 == doze, 2 == quiescent (default)");
+
+#define RATETAB_ENT(_rate, _rateid, _flags) \
+ { \
+ .bitrate = (_rate), \
+ .hw_value = (_rateid), \
+ .flags = (_flags), \
+ }
+
+static struct ieee80211_rate cw1200_rates[] = {
+ RATETAB_ENT(10, 0, 0),
+ RATETAB_ENT(20, 1, 0),
+ RATETAB_ENT(55, 2, 0),
+ RATETAB_ENT(110, 3, 0),
+ RATETAB_ENT(60, 6, 0),
+ RATETAB_ENT(90, 7, 0),
+ RATETAB_ENT(120, 8, 0),
+ RATETAB_ENT(180, 9, 0),
+ RATETAB_ENT(240, 10, 0),
+ RATETAB_ENT(360, 11, 0),
+ RATETAB_ENT(480, 12, 0),
+ RATETAB_ENT(540, 13, 0),
+};
+
+static struct ieee80211_rate cw1200_mcs_rates[] = {
+ RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS),
+};
+
+#define cw1200_a_rates (cw1200_rates + 4)
+#define cw1200_a_rates_size (ARRAY_SIZE(cw1200_rates) - 4)
+#define cw1200_g_rates (cw1200_rates + 0)
+#define cw1200_g_rates_size (ARRAY_SIZE(cw1200_rates))
+#define cw1200_n_rates (cw1200_mcs_rates)
+#define cw1200_n_rates_size (ARRAY_SIZE(cw1200_mcs_rates))
+
+
+#define CHAN2G(_channel, _freq, _flags) { \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+#define CHAN5G(_channel, _flags) { \
+ .band = IEEE80211_BAND_5GHZ, \
+ .center_freq = 5000 + (5 * (_channel)), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+static struct ieee80211_channel cw1200_2ghz_chantable[] = {
+ CHAN2G(1, 2412, 0),
+ CHAN2G(2, 2417, 0),
+ CHAN2G(3, 2422, 0),
+ CHAN2G(4, 2427, 0),
+ CHAN2G(5, 2432, 0),
+ CHAN2G(6, 2437, 0),
+ CHAN2G(7, 2442, 0),
+ CHAN2G(8, 2447, 0),
+ CHAN2G(9, 2452, 0),
+ CHAN2G(10, 2457, 0),
+ CHAN2G(11, 2462, 0),
+ CHAN2G(12, 2467, 0),
+ CHAN2G(13, 2472, 0),
+ CHAN2G(14, 2484, 0),
+};
+
+static struct ieee80211_channel cw1200_5ghz_chantable[] = {
+ CHAN5G(34, 0), CHAN5G(36, 0),
+ CHAN5G(38, 0), CHAN5G(40, 0),
+ CHAN5G(42, 0), CHAN5G(44, 0),
+ CHAN5G(46, 0), CHAN5G(48, 0),
+ CHAN5G(52, 0), CHAN5G(56, 0),
+ CHAN5G(60, 0), CHAN5G(64, 0),
+ CHAN5G(100, 0), CHAN5G(104, 0),
+ CHAN5G(108, 0), CHAN5G(112, 0),
+ CHAN5G(116, 0), CHAN5G(120, 0),
+ CHAN5G(124, 0), CHAN5G(128, 0),
+ CHAN5G(132, 0), CHAN5G(136, 0),
+ CHAN5G(140, 0), CHAN5G(149, 0),
+ CHAN5G(153, 0), CHAN5G(157, 0),
+ CHAN5G(161, 0), CHAN5G(165, 0),
+ CHAN5G(184, 0), CHAN5G(188, 0),
+ CHAN5G(192, 0), CHAN5G(196, 0),
+ CHAN5G(200, 0), CHAN5G(204, 0),
+ CHAN5G(208, 0), CHAN5G(212, 0),
+ CHAN5G(216, 0),
+};
+
+static struct ieee80211_supported_band cw1200_band_2ghz = {
+ .channels = cw1200_2ghz_chantable,
+ .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable),
+ .bitrates = cw1200_g_rates,
+ .n_bitrates = cw1200_g_rates_size,
+ .ht_cap = {
+ .cap = IEEE80211_HT_CAP_GRN_FLD |
+ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
+ IEEE80211_HT_CAP_MAX_AMSDU,
+ .ht_supported = 1,
+ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
+ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
+ .mcs = {
+ .rx_mask[0] = 0xFF,
+ .rx_highest = __cpu_to_le16(0x41),
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+ },
+ },
+};
+
+static struct ieee80211_supported_band cw1200_band_5ghz = {
+ .channels = cw1200_5ghz_chantable,
+ .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable),
+ .bitrates = cw1200_a_rates,
+ .n_bitrates = cw1200_a_rates_size,
+ .ht_cap = {
+ .cap = IEEE80211_HT_CAP_GRN_FLD |
+ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
+ IEEE80211_HT_CAP_MAX_AMSDU,
+ .ht_supported = 1,
+ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
+ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
+ .mcs = {
+ .rx_mask[0] = 0xFF,
+ .rx_highest = __cpu_to_le16(0x41),
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+ },
+ },
+};
+
+static const unsigned long cw1200_ttl[] = {
+ 1 * HZ, /* VO */
+ 2 * HZ, /* VI */
+ 5 * HZ, /* BE */
+ 10 * HZ /* BK */
+};
+
+static const struct ieee80211_ops cw1200_ops = {
+ .start = cw1200_start,
+ .stop = cw1200_stop,
+ .add_interface = cw1200_add_interface,
+ .remove_interface = cw1200_remove_interface,
+ .change_interface = cw1200_change_interface,
+ .tx = cw1200_tx,
+ .hw_scan = cw1200_hw_scan,
+ .set_tim = cw1200_set_tim,
+ .sta_notify = cw1200_sta_notify,
+ .sta_add = cw1200_sta_add,
+ .sta_remove = cw1200_sta_remove,
+ .set_key = cw1200_set_key,
+ .set_rts_threshold = cw1200_set_rts_threshold,
+ .config = cw1200_config,
+ .bss_info_changed = cw1200_bss_info_changed,
+ .prepare_multicast = cw1200_prepare_multicast,
+ .configure_filter = cw1200_configure_filter,
+ .conf_tx = cw1200_conf_tx,
+ .get_stats = cw1200_get_stats,
+ .ampdu_action = cw1200_ampdu_action,
+ .flush = cw1200_flush,
+#ifdef CONFIG_PM
+ .suspend = cw1200_wow_suspend,
+ .resume = cw1200_wow_resume,
+#endif
+ /* Intentionally not offloaded: */
+ /*.channel_switch = cw1200_channel_switch, */
+ /*.remain_on_channel = cw1200_remain_on_channel, */
+ /*.cancel_remain_on_channel = cw1200_cancel_remain_on_channel, */
+};
+
+int cw1200_ba_rx_tids = -1;
+int cw1200_ba_tx_tids = -1;
+module_param(cw1200_ba_rx_tids, int, 0644);
+module_param(cw1200_ba_tx_tids, int, 0644);
+MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs");
+MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs");
+
+static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr,
+ const bool have_5ghz)
+{
+ int i, band;
+ struct ieee80211_hw *hw;
+ struct cw1200_common *priv;
+
+ hw = ieee80211_alloc_hw(sizeof(struct cw1200_common), &cw1200_ops);
+ if (!hw)
+ return NULL;
+
+ priv = hw->priv;
+ priv->hw = hw;
+ priv->hw_type = -1;
+ priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->rates = cw1200_rates; /* TODO: fetch from FW */
+ priv->mcs_rates = cw1200_n_rates;
+ if (cw1200_ba_rx_tids != -1)
+ priv->ba_rx_tid_mask = cw1200_ba_rx_tids;
+ else
+ priv->ba_rx_tid_mask = 0xFF; /* Enable RX BLKACK for all TIDs */
+ if (cw1200_ba_tx_tids != -1)
+ priv->ba_tx_tid_mask = cw1200_ba_tx_tids;
+ else
+ priv->ba_tx_tid_mask = 0xff; /* Enable TX BLKACK for all TIDs */
+
+ hw->flags = IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_SUPPORTS_PS |
+ IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
+ IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+ IEEE80211_HW_SUPPORTS_UAPSD |
+ IEEE80211_HW_CONNECTION_MONITOR |
+ IEEE80211_HW_AMPDU_AGGREGATION |
+ IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
+ IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC;
+
+ hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_MESH_POINT) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO);
+
+#ifdef CONFIG_PM
+ /* Support only for limited wowlan functionalities */
+ hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY |
+ WIPHY_WOWLAN_DISCONNECT;
+ hw->wiphy->wowlan.n_patterns = 0;
+#endif
+
+ hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+
+ hw->channel_change_time = 1000; /* TODO: find actual value */
+ hw->queues = 4;
+
+ priv->rts_threshold = -1;
+
+ hw->max_rates = 8;
+ hw->max_rate_tries = 15;
+ hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM +
+ 8; /* TKIP IV */
+
+ hw->sta_data_size = sizeof(struct cw1200_sta_priv);
+
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &cw1200_band_2ghz;
+ if (have_5ghz)
+ hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz;
+
+ /* Channel params have to be cleared before registering wiphy again */
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ struct ieee80211_supported_band *sband = hw->wiphy->bands[band];
+ if (!sband)
+ continue;
+ for (i = 0; i < sband->n_channels; i++) {
+ sband->channels[i].flags = 0;
+ sband->channels[i].max_antenna_gain = 0;
+ sband->channels[i].max_power = 30;
+ }
+ }
+
+ hw->wiphy->max_scan_ssids = 2;
+ hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+
+ if (macaddr)
+ SET_IEEE80211_PERM_ADDR(hw, (u8 *)macaddr);
+ else
+ SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template);
+
+ /* Fix up mac address if necessary */
+ if (hw->wiphy->perm_addr[3] == 0 &&
+ hw->wiphy->perm_addr[4] == 0 &&
+ hw->wiphy->perm_addr[5] == 0) {
+ get_random_bytes(&hw->wiphy->perm_addr[3], 3);
+ }
+
+ mutex_init(&priv->wsm_cmd_mux);
+ mutex_init(&priv->conf_mutex);
+ priv->workqueue = create_singlethread_workqueue("cw1200_wq");
+ sema_init(&priv->scan.lock, 1);
+ INIT_WORK(&priv->scan.work, cw1200_scan_work);
+ INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work);
+ INIT_DELAYED_WORK(&priv->scan.timeout, cw1200_scan_timeout);
+ INIT_DELAYED_WORK(&priv->clear_recent_scan_work,
+ cw1200_clear_recent_scan_work);
+ INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout);
+ INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work);
+ INIT_WORK(&priv->join_complete_work, cw1200_join_complete_work);
+ INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work);
+ INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work);
+ spin_lock_init(&priv->event_queue_lock);
+ INIT_LIST_HEAD(&priv->event_queue);
+ INIT_WORK(&priv->event_handler, cw1200_event_handler);
+ INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work);
+ INIT_WORK(&priv->bss_params_work, cw1200_bss_params_work);
+ spin_lock_init(&priv->bss_loss_lock);
+ spin_lock_init(&priv->ps_state_lock);
+ INIT_WORK(&priv->set_cts_work, cw1200_set_cts_work);
+ INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work);
+ INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work);
+ INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work);
+ INIT_WORK(&priv->link_id_work, cw1200_link_id_work);
+ INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work);
+ INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset);
+ INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work);
+ INIT_WORK(&priv->set_beacon_wakeup_period_work,
+ cw1200_set_beacon_wakeup_period_work);
+ init_timer(&priv->mcast_timeout);
+ priv->mcast_timeout.data = (unsigned long)priv;
+ priv->mcast_timeout.function = cw1200_mcast_timeout;
+
+ if (cw1200_queue_stats_init(&priv->tx_queue_stats,
+ CW1200_LINK_ID_MAX,
+ cw1200_skb_dtor,
+ priv)) {
+ ieee80211_free_hw(hw);
+ return NULL;
+ }
+
+ for (i = 0; i < 4; ++i) {
+ if (cw1200_queue_init(&priv->tx_queue[i],
+ &priv->tx_queue_stats, i, 16,
+ cw1200_ttl[i])) {
+ for (; i > 0; i--)
+ cw1200_queue_deinit(&priv->tx_queue[i - 1]);
+ cw1200_queue_stats_deinit(&priv->tx_queue_stats);
+ ieee80211_free_hw(hw);
+ return NULL;
+ }
+ }
+
+ init_waitqueue_head(&priv->channel_switch_done);
+ init_waitqueue_head(&priv->wsm_cmd_wq);
+ init_waitqueue_head(&priv->wsm_startup_done);
+ init_waitqueue_head(&priv->ps_mode_switch_done);
+ wsm_buf_init(&priv->wsm_cmd_buf);
+ spin_lock_init(&priv->wsm_cmd.lock);
+ priv->wsm_cmd.done = 1;
+ tx_policy_init(priv);
+
+ return hw;
+}
+
+static int cw1200_register_common(struct ieee80211_hw *dev)
+{
+ struct cw1200_common *priv = dev->priv;
+ int err;
+
+#ifdef CONFIG_PM
+ err = cw1200_pm_init(&priv->pm_state, priv);
+ if (err) {
+ pr_err("Cannot init PM. (%d).\n",
+ err);
+ return err;
+ }
+#endif
+
+ err = ieee80211_register_hw(dev);
+ if (err) {
+ pr_err("Cannot register device (%d).\n",
+ err);
+#ifdef CONFIG_PM
+ cw1200_pm_deinit(&priv->pm_state);
+#endif
+ return err;
+ }
+
+ cw1200_debug_init(priv);
+
+ pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy));
+ return 0;
+}
+
+static void cw1200_free_common(struct ieee80211_hw *dev)
+{
+ ieee80211_free_hw(dev);
+}
+
+static void cw1200_unregister_common(struct ieee80211_hw *dev)
+{
+ struct cw1200_common *priv = dev->priv;
+ int i;
+
+ ieee80211_unregister_hw(dev);
+
+ del_timer_sync(&priv->mcast_timeout);
+ cw1200_unregister_bh(priv);
+
+ cw1200_debug_release(priv);
+
+ mutex_destroy(&priv->conf_mutex);
+
+ wsm_buf_deinit(&priv->wsm_cmd_buf);
+
+ destroy_workqueue(priv->workqueue);
+ priv->workqueue = NULL;
+
+ if (priv->sdd) {
+ release_firmware(priv->sdd);
+ priv->sdd = NULL;
+ }
+
+ for (i = 0; i < 4; ++i)
+ cw1200_queue_deinit(&priv->tx_queue[i]);
+
+ cw1200_queue_stats_deinit(&priv->tx_queue_stats);
+#ifdef CONFIG_PM
+ cw1200_pm_deinit(&priv->pm_state);
+#endif
+}
+
+/* Clock is in KHz */
+u32 cw1200_dpll_from_clk(u16 clk_khz)
+{
+ switch (clk_khz) {
+ case 0x32C8: /* 13000 KHz */
+ return 0x1D89D241;
+ case 0x3E80: /* 16000 KHz */
+ return 0x000001E1;
+ case 0x41A0: /* 16800 KHz */
+ return 0x124931C1;
+ case 0x4B00: /* 19200 KHz */
+ return 0x00000191;
+ case 0x5DC0: /* 24000 KHz */
+ return 0x00000141;
+ case 0x6590: /* 26000 KHz */
+ return 0x0EC4F121;
+ case 0x8340: /* 33600 KHz */
+ return 0x092490E1;
+ case 0x9600: /* 38400 KHz */
+ return 0x100010C1;
+ case 0x9C40: /* 40000 KHz */
+ return 0x000000C1;
+ case 0xBB80: /* 48000 KHz */
+ return 0x000000A1;
+ case 0xCB20: /* 52000 KHz */
+ return 0x07627091;
+ default:
+ pr_err("Unknown Refclk freq (0x%04x), using 2600KHz\n",
+ clk_khz);
+ return 0x0EC4F121;
+ }
+}
+
+int cw1200_core_probe(const struct hwbus_ops *hwbus_ops,
+ struct hwbus_priv *hwbus,
+ struct device *pdev,
+ struct cw1200_common **core,
+ int ref_clk, const u8 *macaddr,
+ const char *sdd_path, bool have_5ghz)
+{
+ int err = -EINVAL;
+ struct ieee80211_hw *dev;
+ struct cw1200_common *priv;
+ struct wsm_operational_mode mode = {
+ .power_mode = cw1200_power_mode,
+ .disable_more_flag_usage = true,
+ };
+
+ dev = cw1200_init_common(macaddr, have_5ghz);
+ if (!dev)
+ goto err;
+
+ priv = dev->priv;
+ priv->hw_refclk = ref_clk;
+ if (cw1200_refclk)
+ priv->hw_refclk = cw1200_refclk;
+
+ priv->sdd_path = (char *)sdd_path;
+ if (cw1200_sdd_path)
+ priv->sdd_path = cw1200_sdd_path;
+
+ priv->hwbus_ops = hwbus_ops;
+ priv->hwbus_priv = hwbus;
+ priv->pdev = pdev;
+ SET_IEEE80211_DEV(priv->hw, pdev);
+
+ /* Pass struct cw1200_common back up */
+ *core = priv;
+
+ err = cw1200_register_bh(priv);
+ if (err)
+ goto err1;
+
+ err = cw1200_load_firmware(priv);
+ if (err)
+ goto err2;
+
+ if (wait_event_interruptible_timeout(priv->wsm_startup_done,
+ priv->firmware_ready,
+ 3*HZ) <= 0) {
+ /* TODO: Need to find how to reset device
+ in QUEUE mode properly.
+ */
+ pr_err("Timeout waiting on device startup\n");
+ err = -ETIMEDOUT;
+ goto err2;
+ }
+
+ /* Set low-power mode. */
+ wsm_set_operational_mode(priv, &mode);
+
+ /* Enable multi-TX confirmation */
+ wsm_use_multi_tx_conf(priv, true);
+
+ err = cw1200_register_common(dev);
+ if (err)
+ goto err2;
+
+ return err;
+
+err2:
+ cw1200_unregister_bh(priv);
+err1:
+ cw1200_free_common(dev);
+err:
+ *core = NULL;
+ return err;
+}
+EXPORT_SYMBOL_GPL(cw1200_core_probe);
+
+void cw1200_core_release(struct cw1200_common *self)
+{
+ /* Disable device interrupts */
+ self->hwbus_ops->lock(self->hwbus_priv);
+ __cw1200_irq_enable(self, 0);
+ self->hwbus_ops->unlock(self->hwbus_priv);
+
+ /* And then clean up */
+ cw1200_unregister_common(self->hw);
+ cw1200_free_common(self->hw);
+ return;
+}
+EXPORT_SYMBOL_GPL(cw1200_core_release);
diff --git a/drivers/net/wireless/cw1200/pm.c b/drivers/net/wireless/cw1200/pm.c
new file mode 100644
index 000000000000..b37abb9f0453
--- /dev/null
+++ b/drivers/net/wireless/cw1200/pm.c
@@ -0,0 +1,367 @@
+/*
+ * Mac80211 power management API for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2011, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/if_ether.h>
+#include "cw1200.h"
+#include "pm.h"
+#include "sta.h"
+#include "bh.h"
+#include "hwbus.h"
+
+#define CW1200_BEACON_SKIPPING_MULTIPLIER 3
+
+struct cw1200_udp_port_filter {
+ struct wsm_udp_port_filter_hdr hdr;
+ /* Up to 4 filters are allowed. */
+ struct wsm_udp_port_filter filters[WSM_MAX_FILTER_ELEMENTS];
+} __packed;
+
+struct cw1200_ether_type_filter {
+ struct wsm_ether_type_filter_hdr hdr;
+ /* Up to 4 filters are allowed. */
+ struct wsm_ether_type_filter filters[WSM_MAX_FILTER_ELEMENTS];
+} __packed;
+
+static struct cw1200_udp_port_filter cw1200_udp_port_filter_on = {
+ .hdr.num = 2,
+ .filters = {
+ [0] = {
+ .action = WSM_FILTER_ACTION_FILTER_OUT,
+ .type = WSM_FILTER_PORT_TYPE_DST,
+ .port = __cpu_to_le16(67), /* DHCP Bootps */
+ },
+ [1] = {
+ .action = WSM_FILTER_ACTION_FILTER_OUT,
+ .type = WSM_FILTER_PORT_TYPE_DST,
+ .port = __cpu_to_le16(68), /* DHCP Bootpc */
+ },
+ }
+};
+
+static struct wsm_udp_port_filter_hdr cw1200_udp_port_filter_off = {
+ .num = 0,
+};
+
+#ifndef ETH_P_WAPI
+#define ETH_P_WAPI 0x88B4
+#endif
+
+static struct cw1200_ether_type_filter cw1200_ether_type_filter_on = {
+ .hdr.num = 4,
+ .filters = {
+ [0] = {
+ .action = WSM_FILTER_ACTION_FILTER_IN,
+ .type = __cpu_to_le16(ETH_P_IP),
+ },
+ [1] = {
+ .action = WSM_FILTER_ACTION_FILTER_IN,
+ .type = __cpu_to_le16(ETH_P_PAE),
+ },
+ [2] = {
+ .action = WSM_FILTER_ACTION_FILTER_IN,
+ .type = __cpu_to_le16(ETH_P_WAPI),
+ },
+ [3] = {
+ .action = WSM_FILTER_ACTION_FILTER_IN,
+ .type = __cpu_to_le16(ETH_P_ARP),
+ },
+ },
+};
+
+static struct wsm_ether_type_filter_hdr cw1200_ether_type_filter_off = {
+ .num = 0,
+};
+
+/* private */
+struct cw1200_suspend_state {
+ unsigned long bss_loss_tmo;
+ unsigned long join_tmo;
+ unsigned long direct_probe;
+ unsigned long link_id_gc;
+ bool beacon_skipping;
+ u8 prev_ps_mode;
+};
+
+static void cw1200_pm_stay_awake_tmo(unsigned long arg)
+{
+ /* XXX what's the point of this ? */
+}
+
+int cw1200_pm_init(struct cw1200_pm_state *pm,
+ struct cw1200_common *priv)
+{
+ spin_lock_init(&pm->lock);
+
+ init_timer(&pm->stay_awake);
+ pm->stay_awake.data = (unsigned long)pm;
+ pm->stay_awake.function = cw1200_pm_stay_awake_tmo;
+
+ return 0;
+}
+
+void cw1200_pm_deinit(struct cw1200_pm_state *pm)
+{
+ del_timer_sync(&pm->stay_awake);
+}
+
+void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
+ unsigned long tmo)
+{
+ long cur_tmo;
+ spin_lock_bh(&pm->lock);
+ cur_tmo = pm->stay_awake.expires - jiffies;
+ if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo)
+ mod_timer(&pm->stay_awake, jiffies + tmo);
+ spin_unlock_bh(&pm->lock);
+}
+
+static long cw1200_suspend_work(struct delayed_work *work)
+{
+ int ret = cancel_delayed_work(work);
+ long tmo;
+ if (ret > 0) {
+ /* Timer is pending */
+ tmo = work->timer.expires - jiffies;
+ if (tmo < 0)
+ tmo = 0;
+ } else {
+ tmo = -1;
+ }
+ return tmo;
+}
+
+static int cw1200_resume_work(struct cw1200_common *priv,
+ struct delayed_work *work,
+ unsigned long tmo)
+{
+ if ((long)tmo < 0)
+ return 1;
+
+ return queue_delayed_work(priv->workqueue, work, tmo);
+}
+
+int cw1200_can_suspend(struct cw1200_common *priv)
+{
+ if (atomic_read(&priv->bh_rx)) {
+ wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n");
+ return 0;
+ }
+ return 1;
+}
+EXPORT_SYMBOL_GPL(cw1200_can_suspend);
+
+int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+{
+ struct cw1200_common *priv = hw->priv;
+ struct cw1200_pm_state *pm_state = &priv->pm_state;
+ struct cw1200_suspend_state *state;
+ int ret;
+
+ spin_lock_bh(&pm_state->lock);
+ ret = timer_pending(&pm_state->stay_awake);
+ spin_unlock_bh(&pm_state->lock);
+ if (ret)
+ return -EAGAIN;
+
+ /* Do not suspend when datapath is not idle */
+ if (priv->tx_queue_stats.num_queued)
+ return -EBUSY;
+
+ /* Make sure there is no configuration requests in progress. */
+ if (!mutex_trylock(&priv->conf_mutex))
+ return -EBUSY;
+
+ /* Ensure pending operations are done.
+ * Note also that wow_suspend must return in ~2.5sec, before
+ * watchdog is triggered.
+ */
+ if (priv->channel_switch_in_progress)
+ goto revert1;
+
+ /* Do not suspend when join is pending */
+ if (priv->join_pending)
+ goto revert1;
+
+ /* Do not suspend when scanning */
+ if (down_trylock(&priv->scan.lock))
+ goto revert1;
+
+ /* Lock TX. */
+ wsm_lock_tx_async(priv);
+
+ /* Wait to avoid possible race with bh code.
+ * But do not wait too long...
+ */
+ if (wait_event_timeout(priv->bh_evt_wq,
+ !priv->hw_bufs_used, HZ / 10) <= 0)
+ goto revert2;
+
+ /* Set UDP filter */
+ wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_on.hdr);
+
+ /* Set ethernet frame type filter */
+ wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_on.hdr);
+
+ /* Allocate state */
+ state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL);
+ if (!state)
+ goto revert3;
+
+ /* Change to legacy PS while going to suspend */
+ if (!priv->vif->p2p &&
+ priv->join_status == CW1200_JOIN_STATUS_STA &&
+ priv->powersave_mode.mode != WSM_PSM_PS) {
+ state->prev_ps_mode = priv->powersave_mode.mode;
+ priv->powersave_mode.mode = WSM_PSM_PS;
+ cw1200_set_pm(priv, &priv->powersave_mode);
+ if (wait_event_interruptible_timeout(priv->ps_mode_switch_done,
+ !priv->ps_mode_switch_in_progress, 1*HZ) <= 0) {
+ goto revert3;
+ }
+ }
+
+ /* Store delayed work states. */
+ state->bss_loss_tmo =
+ cw1200_suspend_work(&priv->bss_loss_work);
+ state->join_tmo =
+ cw1200_suspend_work(&priv->join_timeout);
+ state->direct_probe =
+ cw1200_suspend_work(&priv->scan.probe_work);
+ state->link_id_gc =
+ cw1200_suspend_work(&priv->link_id_gc_work);
+
+ cancel_delayed_work_sync(&priv->clear_recent_scan_work);
+ atomic_set(&priv->recent_scan, 0);
+
+ /* Enable beacon skipping */
+ if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+ priv->join_dtim_period &&
+ !priv->has_multicast_subscription) {
+ state->beacon_skipping = true;
+ wsm_set_beacon_wakeup_period(priv,
+ priv->join_dtim_period,
+ CW1200_BEACON_SKIPPING_MULTIPLIER * priv->join_dtim_period);
+ }
+
+ /* Stop serving thread */
+ if (cw1200_bh_suspend(priv))
+ goto revert4;
+
+ ret = timer_pending(&priv->mcast_timeout);
+ if (ret)
+ goto revert5;
+
+ /* Store suspend state */
+ pm_state->suspend_state = state;
+
+ /* Enable IRQ wake */
+ ret = priv->hwbus_ops->power_mgmt(priv->hwbus_priv, true);
+ if (ret) {
+ wiphy_err(priv->hw->wiphy,
+ "PM request failed: %d. WoW is disabled.\n", ret);
+ cw1200_wow_resume(hw);
+ return -EBUSY;
+ }
+
+ /* Force resume if event is coming from the device. */
+ if (atomic_read(&priv->bh_rx)) {
+ cw1200_wow_resume(hw);
+ return -EAGAIN;
+ }
+
+ return 0;
+
+revert5:
+ WARN_ON(cw1200_bh_resume(priv));
+revert4:
+ cw1200_resume_work(priv, &priv->bss_loss_work,
+ state->bss_loss_tmo);
+ cw1200_resume_work(priv, &priv->join_timeout,
+ state->join_tmo);
+ cw1200_resume_work(priv, &priv->scan.probe_work,
+ state->direct_probe);
+ cw1200_resume_work(priv, &priv->link_id_gc_work,
+ state->link_id_gc);
+ kfree(state);
+revert3:
+ wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
+ wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off);
+revert2:
+ wsm_unlock_tx(priv);
+ up(&priv->scan.lock);
+revert1:
+ mutex_unlock(&priv->conf_mutex);
+ return -EBUSY;
+}
+
+int cw1200_wow_resume(struct ieee80211_hw *hw)
+{
+ struct cw1200_common *priv = hw->priv;
+ struct cw1200_pm_state *pm_state = &priv->pm_state;
+ struct cw1200_suspend_state *state;
+
+ state = pm_state->suspend_state;
+ pm_state->suspend_state = NULL;
+
+ /* Disable IRQ wake */
+ priv->hwbus_ops->power_mgmt(priv->hwbus_priv, false);
+
+ /* Scan.lock must be released before BH is resumed other way
+ * in case when BSS_LOST command arrived the processing of the
+ * command will be delayed.
+ */
+ up(&priv->scan.lock);
+
+ /* Resume BH thread */
+ WARN_ON(cw1200_bh_resume(priv));
+
+ /* Restores previous PS mode */
+ if (!priv->vif->p2p && priv->join_status == CW1200_JOIN_STATUS_STA) {
+ priv->powersave_mode.mode = state->prev_ps_mode;
+ cw1200_set_pm(priv, &priv->powersave_mode);
+ }
+
+ if (state->beacon_skipping) {
+ wsm_set_beacon_wakeup_period(priv, priv->beacon_int *
+ priv->join_dtim_period >
+ MAX_BEACON_SKIP_TIME_MS ? 1 :
+ priv->join_dtim_period, 0);
+ state->beacon_skipping = false;
+ }
+
+ /* Resume delayed work */
+ cw1200_resume_work(priv, &priv->bss_loss_work,
+ state->bss_loss_tmo);
+ cw1200_resume_work(priv, &priv->join_timeout,
+ state->join_tmo);
+ cw1200_resume_work(priv, &priv->scan.probe_work,
+ state->direct_probe);
+ cw1200_resume_work(priv, &priv->link_id_gc_work,
+ state->link_id_gc);
+
+ /* Remove UDP port filter */
+ wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
+
+ /* Remove ethernet frame type filter */
+ wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off);
+
+ /* Unlock datapath */
+ wsm_unlock_tx(priv);
+
+ /* Unlock configuration mutex */
+ mutex_unlock(&priv->conf_mutex);
+
+ /* Free memory */
+ kfree(state);
+
+ return 0;
+}
diff --git a/drivers/net/wireless/cw1200/pm.h b/drivers/net/wireless/cw1200/pm.h
new file mode 100644
index 000000000000..3ed90ff22bb8
--- /dev/null
+++ b/drivers/net/wireless/cw1200/pm.h
@@ -0,0 +1,43 @@
+/*
+ * Mac80211 power management interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2011, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 PM_H_INCLUDED
+#define PM_H_INCLUDED
+
+/* ******************************************************************** */
+/* mac80211 API */
+
+/* extern */ struct cw1200_common;
+/* private */ struct cw1200_suspend_state;
+
+struct cw1200_pm_state {
+ struct cw1200_suspend_state *suspend_state;
+ struct timer_list stay_awake;
+ struct platform_device *pm_dev;
+ spinlock_t lock; /* Protect access */
+};
+
+#ifdef CONFIG_PM
+int cw1200_pm_init(struct cw1200_pm_state *pm,
+ struct cw1200_common *priv);
+void cw1200_pm_deinit(struct cw1200_pm_state *pm);
+int cw1200_wow_suspend(struct ieee80211_hw *hw,
+ struct cfg80211_wowlan *wowlan);
+int cw1200_wow_resume(struct ieee80211_hw *hw);
+int cw1200_can_suspend(struct cw1200_common *priv);
+void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
+ unsigned long tmo);
+#else
+static inline void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
+ unsigned long tmo) {
+}
+#endif
+#endif
diff --git a/drivers/net/wireless/cw1200/queue.c b/drivers/net/wireless/cw1200/queue.c
new file mode 100644
index 000000000000..8510454d5db1
--- /dev/null
+++ b/drivers/net/wireless/cw1200/queue.c
@@ -0,0 +1,583 @@
+/*
+ * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <net/mac80211.h>
+#include <linux/sched.h>
+#include "queue.h"
+#include "cw1200.h"
+#include "debug.h"
+
+/* private */ struct cw1200_queue_item
+{
+ struct list_head head;
+ struct sk_buff *skb;
+ u32 packet_id;
+ unsigned long queue_timestamp;
+ unsigned long xmit_timestamp;
+ struct cw1200_txpriv txpriv;
+ u8 generation;
+};
+
+static inline void __cw1200_queue_lock(struct cw1200_queue *queue)
+{
+ struct cw1200_queue_stats *stats = queue->stats;
+ if (queue->tx_locked_cnt++ == 0) {
+ pr_debug("[TX] Queue %d is locked.\n",
+ queue->queue_id);
+ ieee80211_stop_queue(stats->priv->hw, queue->queue_id);
+ }
+}
+
+static inline void __cw1200_queue_unlock(struct cw1200_queue *queue)
+{
+ struct cw1200_queue_stats *stats = queue->stats;
+ BUG_ON(!queue->tx_locked_cnt);
+ if (--queue->tx_locked_cnt == 0) {
+ pr_debug("[TX] Queue %d is unlocked.\n",
+ queue->queue_id);
+ ieee80211_wake_queue(stats->priv->hw, queue->queue_id);
+ }
+}
+
+static inline void cw1200_queue_parse_id(u32 packet_id, u8 *queue_generation,
+ u8 *queue_id, u8 *item_generation,
+ u8 *item_id)
+{
+ *item_id = (packet_id >> 0) & 0xFF;
+ *item_generation = (packet_id >> 8) & 0xFF;
+ *queue_id = (packet_id >> 16) & 0xFF;
+ *queue_generation = (packet_id >> 24) & 0xFF;
+}
+
+static inline u32 cw1200_queue_mk_packet_id(u8 queue_generation, u8 queue_id,
+ u8 item_generation, u8 item_id)
+{
+ return ((u32)item_id << 0) |
+ ((u32)item_generation << 8) |
+ ((u32)queue_id << 16) |
+ ((u32)queue_generation << 24);
+}
+
+static void cw1200_queue_post_gc(struct cw1200_queue_stats *stats,
+ struct list_head *gc_list)
+{
+ struct cw1200_queue_item *item, *tmp;
+
+ list_for_each_entry_safe(item, tmp, gc_list, head) {
+ list_del(&item->head);
+ stats->skb_dtor(stats->priv, item->skb, &item->txpriv);
+ kfree(item);
+ }
+}
+
+static void cw1200_queue_register_post_gc(struct list_head *gc_list,
+ struct cw1200_queue_item *item)
+{
+ struct cw1200_queue_item *gc_item;
+ gc_item = kmalloc(sizeof(struct cw1200_queue_item),
+ GFP_ATOMIC);
+ BUG_ON(!gc_item);
+ memcpy(gc_item, item, sizeof(struct cw1200_queue_item));
+ list_add_tail(&gc_item->head, gc_list);
+}
+
+static void __cw1200_queue_gc(struct cw1200_queue *queue,
+ struct list_head *head,
+ bool unlock)
+{
+ struct cw1200_queue_stats *stats = queue->stats;
+ struct cw1200_queue_item *item = NULL, *tmp;
+ bool wakeup_stats = false;
+
+ list_for_each_entry_safe(item, tmp, &queue->queue, head) {
+ if (jiffies - item->queue_timestamp < queue->ttl)
+ break;
+ --queue->num_queued;
+ --queue->link_map_cache[item->txpriv.link_id];
+ spin_lock_bh(&stats->lock);
+ --stats->num_queued;
+ if (!--stats->link_map_cache[item->txpriv.link_id])
+ wakeup_stats = true;
+ spin_unlock_bh(&stats->lock);
+ cw1200_debug_tx_ttl(stats->priv);
+ cw1200_queue_register_post_gc(head, item);
+ item->skb = NULL;
+ list_move_tail(&item->head, &queue->free_pool);
+ }
+
+ if (wakeup_stats)
+ wake_up(&stats->wait_link_id_empty);
+
+ if (queue->overfull) {
+ if (queue->num_queued <= (queue->capacity >> 1)) {
+ queue->overfull = false;
+ if (unlock)
+ __cw1200_queue_unlock(queue);
+ } else if (item) {
+ unsigned long tmo = item->queue_timestamp + queue->ttl;
+ mod_timer(&queue->gc, tmo);
+ cw1200_pm_stay_awake(&stats->priv->pm_state,
+ tmo - jiffies);
+ }
+ }
+}
+
+static void cw1200_queue_gc(unsigned long arg)
+{
+ LIST_HEAD(list);
+ struct cw1200_queue *queue =
+ (struct cw1200_queue *)arg;
+
+ spin_lock_bh(&queue->lock);
+ __cw1200_queue_gc(queue, &list, true);
+ spin_unlock_bh(&queue->lock);
+ cw1200_queue_post_gc(queue->stats, &list);
+}
+
+int cw1200_queue_stats_init(struct cw1200_queue_stats *stats,
+ size_t map_capacity,
+ cw1200_queue_skb_dtor_t skb_dtor,
+ struct cw1200_common *priv)
+{
+ memset(stats, 0, sizeof(*stats));
+ stats->map_capacity = map_capacity;
+ stats->skb_dtor = skb_dtor;
+ stats->priv = priv;
+ spin_lock_init(&stats->lock);
+ init_waitqueue_head(&stats->wait_link_id_empty);
+
+ stats->link_map_cache = kzalloc(sizeof(int) * map_capacity,
+ GFP_KERNEL);
+ if (!stats->link_map_cache)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int cw1200_queue_init(struct cw1200_queue *queue,
+ struct cw1200_queue_stats *stats,
+ u8 queue_id,
+ size_t capacity,
+ unsigned long ttl)
+{
+ size_t i;
+
+ memset(queue, 0, sizeof(*queue));
+ queue->stats = stats;
+ queue->capacity = capacity;
+ queue->queue_id = queue_id;
+ queue->ttl = ttl;
+ INIT_LIST_HEAD(&queue->queue);
+ INIT_LIST_HEAD(&queue->pending);
+ INIT_LIST_HEAD(&queue->free_pool);
+ spin_lock_init(&queue->lock);
+ init_timer(&queue->gc);
+ queue->gc.data = (unsigned long)queue;
+ queue->gc.function = cw1200_queue_gc;
+
+ queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity,
+ GFP_KERNEL);
+ if (!queue->pool)
+ return -ENOMEM;
+
+ queue->link_map_cache = kzalloc(sizeof(int) * stats->map_capacity,
+ GFP_KERNEL);
+ if (!queue->link_map_cache) {
+ kfree(queue->pool);
+ queue->pool = NULL;
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < capacity; ++i)
+ list_add_tail(&queue->pool[i].head, &queue->free_pool);
+
+ return 0;
+}
+
+int cw1200_queue_clear(struct cw1200_queue *queue)
+{
+ int i;
+ LIST_HEAD(gc_list);
+ struct cw1200_queue_stats *stats = queue->stats;
+ struct cw1200_queue_item *item, *tmp;
+
+ spin_lock_bh(&queue->lock);
+ queue->generation++;
+ list_splice_tail_init(&queue->queue, &queue->pending);
+ list_for_each_entry_safe(item, tmp, &queue->pending, head) {
+ WARN_ON(!item->skb);
+ cw1200_queue_register_post_gc(&gc_list, item);
+ item->skb = NULL;
+ list_move_tail(&item->head, &queue->free_pool);
+ }
+ queue->num_queued = 0;
+ queue->num_pending = 0;
+
+ spin_lock_bh(&stats->lock);
+ for (i = 0; i < stats->map_capacity; ++i) {
+ stats->num_queued -= queue->link_map_cache[i];
+ stats->link_map_cache[i] -= queue->link_map_cache[i];
+ queue->link_map_cache[i] = 0;
+ }
+ spin_unlock_bh(&stats->lock);
+ if (queue->overfull) {
+ queue->overfull = false;
+ __cw1200_queue_unlock(queue);
+ }
+ spin_unlock_bh(&queue->lock);
+ wake_up(&stats->wait_link_id_empty);
+ cw1200_queue_post_gc(stats, &gc_list);
+ return 0;
+}
+
+void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats)
+{
+ kfree(stats->link_map_cache);
+ stats->link_map_cache = NULL;
+}
+
+void cw1200_queue_deinit(struct cw1200_queue *queue)
+{
+ cw1200_queue_clear(queue);
+ del_timer_sync(&queue->gc);
+ INIT_LIST_HEAD(&queue->free_pool);
+ kfree(queue->pool);
+ kfree(queue->link_map_cache);
+ queue->pool = NULL;
+ queue->link_map_cache = NULL;
+ queue->capacity = 0;
+}
+
+size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue,
+ u32 link_id_map)
+{
+ size_t ret;
+ int i, bit;
+ size_t map_capacity = queue->stats->map_capacity;
+
+ if (!link_id_map)
+ return 0;
+
+ spin_lock_bh(&queue->lock);
+ if (link_id_map == (u32)-1) {
+ ret = queue->num_queued - queue->num_pending;
+ } else {
+ ret = 0;
+ for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) {
+ if (link_id_map & bit)
+ ret += queue->link_map_cache[i];
+ }
+ }
+ spin_unlock_bh(&queue->lock);
+ return ret;
+}
+
+int cw1200_queue_put(struct cw1200_queue *queue,
+ struct sk_buff *skb,
+ struct cw1200_txpriv *txpriv)
+{
+ int ret = 0;
+ LIST_HEAD(gc_list);
+ struct cw1200_queue_stats *stats = queue->stats;
+
+ if (txpriv->link_id >= queue->stats->map_capacity)
+ return -EINVAL;
+
+ spin_lock_bh(&queue->lock);
+ if (!WARN_ON(list_empty(&queue->free_pool))) {
+ struct cw1200_queue_item *item = list_first_entry(
+ &queue->free_pool, struct cw1200_queue_item, head);
+ BUG_ON(item->skb);
+
+ list_move_tail(&item->head, &queue->queue);
+ item->skb = skb;
+ item->txpriv = *txpriv;
+ item->generation = 0;
+ item->packet_id = cw1200_queue_mk_packet_id(queue->generation,
+ queue->queue_id,
+ item->generation,
+ item - queue->pool);
+ item->queue_timestamp = jiffies;
+
+ ++queue->num_queued;
+ ++queue->link_map_cache[txpriv->link_id];
+
+ spin_lock_bh(&stats->lock);
+ ++stats->num_queued;
+ ++stats->link_map_cache[txpriv->link_id];
+ spin_unlock_bh(&stats->lock);
+
+ /* TX may happen in parallel sometimes.
+ * Leave extra queue slots so we don't overflow.
+ */
+ if (queue->overfull == false &&
+ queue->num_queued >=
+ (queue->capacity - (num_present_cpus() - 1))) {
+ queue->overfull = true;
+ __cw1200_queue_lock(queue);
+ mod_timer(&queue->gc, jiffies);
+ }
+ } else {
+ ret = -ENOENT;
+ }
+ spin_unlock_bh(&queue->lock);
+ return ret;
+}
+
+int cw1200_queue_get(struct cw1200_queue *queue,
+ u32 link_id_map,
+ struct wsm_tx **tx,
+ struct ieee80211_tx_info **tx_info,
+ const struct cw1200_txpriv **txpriv)
+{
+ int ret = -ENOENT;
+ struct cw1200_queue_item *item;
+ struct cw1200_queue_stats *stats = queue->stats;
+ bool wakeup_stats = false;
+
+ spin_lock_bh(&queue->lock);
+ list_for_each_entry(item, &queue->queue, head) {
+ if (link_id_map & BIT(item->txpriv.link_id)) {
+ ret = 0;
+ break;
+ }
+ }
+
+ if (!WARN_ON(ret)) {
+ *tx = (struct wsm_tx *)item->skb->data;
+ *tx_info = IEEE80211_SKB_CB(item->skb);
+ *txpriv = &item->txpriv;
+ (*tx)->packet_id = __cpu_to_le32(item->packet_id);
+ list_move_tail(&item->head, &queue->pending);
+ ++queue->num_pending;
+ --queue->link_map_cache[item->txpriv.link_id];
+ item->xmit_timestamp = jiffies;
+
+ spin_lock_bh(&stats->lock);
+ --stats->num_queued;
+ if (!--stats->link_map_cache[item->txpriv.link_id])
+ wakeup_stats = true;
+ spin_unlock_bh(&stats->lock);
+ }
+ spin_unlock_bh(&queue->lock);
+ if (wakeup_stats)
+ wake_up(&stats->wait_link_id_empty);
+ return ret;
+}
+
+int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id)
+{
+ int ret = 0;
+ u8 queue_generation, queue_id, item_generation, item_id;
+ struct cw1200_queue_item *item;
+ struct cw1200_queue_stats *stats = queue->stats;
+
+ cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
+ &item_generation, &item_id);
+
+ item = &queue->pool[item_id];
+
+ spin_lock_bh(&queue->lock);
+ BUG_ON(queue_id != queue->queue_id);
+ if (queue_generation != queue->generation) {
+ ret = -ENOENT;
+ } else if (item_id >= (unsigned) queue->capacity) {
+ WARN_ON(1);
+ ret = -EINVAL;
+ } else if (item->generation != item_generation) {
+ WARN_ON(1);
+ ret = -ENOENT;
+ } else {
+ --queue->num_pending;
+ ++queue->link_map_cache[item->txpriv.link_id];
+
+ spin_lock_bh(&stats->lock);
+ ++stats->num_queued;
+ ++stats->link_map_cache[item->txpriv.link_id];
+ spin_unlock_bh(&stats->lock);
+
+ item->generation = ++item_generation;
+ item->packet_id = cw1200_queue_mk_packet_id(queue_generation,
+ queue_id,
+ item_generation,
+ item_id);
+ list_move(&item->head, &queue->queue);
+ }
+ spin_unlock_bh(&queue->lock);
+ return ret;
+}
+
+int cw1200_queue_requeue_all(struct cw1200_queue *queue)
+{
+ struct cw1200_queue_item *item, *tmp;
+ struct cw1200_queue_stats *stats = queue->stats;
+ spin_lock_bh(&queue->lock);
+
+ list_for_each_entry_safe_reverse(item, tmp, &queue->pending, head) {
+ --queue->num_pending;
+ ++queue->link_map_cache[item->txpriv.link_id];
+
+ spin_lock_bh(&stats->lock);
+ ++stats->num_queued;
+ ++stats->link_map_cache[item->txpriv.link_id];
+ spin_unlock_bh(&stats->lock);
+
+ ++item->generation;
+ item->packet_id = cw1200_queue_mk_packet_id(queue->generation,
+ queue->queue_id,
+ item->generation,
+ item - queue->pool);
+ list_move(&item->head, &queue->queue);
+ }
+ spin_unlock_bh(&queue->lock);
+
+ return 0;
+}
+
+int cw1200_queue_remove(struct cw1200_queue *queue, u32 packet_id)
+{
+ int ret = 0;
+ u8 queue_generation, queue_id, item_generation, item_id;
+ struct cw1200_queue_item *item;
+ struct cw1200_queue_stats *stats = queue->stats;
+ struct sk_buff *gc_skb = NULL;
+ struct cw1200_txpriv gc_txpriv;
+
+ cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
+ &item_generation, &item_id);
+
+ item = &queue->pool[item_id];
+
+ spin_lock_bh(&queue->lock);
+ BUG_ON(queue_id != queue->queue_id);
+ if (queue_generation != queue->generation) {
+ ret = -ENOENT;
+ } else if (item_id >= (unsigned) queue->capacity) {
+ WARN_ON(1);
+ ret = -EINVAL;
+ } else if (item->generation != item_generation) {
+ WARN_ON(1);
+ ret = -ENOENT;
+ } else {
+ gc_txpriv = item->txpriv;
+ gc_skb = item->skb;
+ item->skb = NULL;
+ --queue->num_pending;
+ --queue->num_queued;
+ ++queue->num_sent;
+ ++item->generation;
+ /* Do not use list_move_tail here, but list_move:
+ * try to utilize cache row.
+ */
+ list_move(&item->head, &queue->free_pool);
+
+ if (queue->overfull &&
+ (queue->num_queued <= (queue->capacity >> 1))) {
+ queue->overfull = false;
+ __cw1200_queue_unlock(queue);
+ }
+ }
+ spin_unlock_bh(&queue->lock);
+
+ if (gc_skb)
+ stats->skb_dtor(stats->priv, gc_skb, &gc_txpriv);
+
+ return ret;
+}
+
+int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id,
+ struct sk_buff **skb,
+ const struct cw1200_txpriv **txpriv)
+{
+ int ret = 0;
+ u8 queue_generation, queue_id, item_generation, item_id;
+ struct cw1200_queue_item *item;
+ cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
+ &item_generation, &item_id);
+
+ item = &queue->pool[item_id];
+
+ spin_lock_bh(&queue->lock);
+ BUG_ON(queue_id != queue->queue_id);
+ if (queue_generation != queue->generation) {
+ ret = -ENOENT;
+ } else if (item_id >= (unsigned) queue->capacity) {
+ WARN_ON(1);
+ ret = -EINVAL;
+ } else if (item->generation != item_generation) {
+ WARN_ON(1);
+ ret = -ENOENT;
+ } else {
+ *skb = item->skb;
+ *txpriv = &item->txpriv;
+ }
+ spin_unlock_bh(&queue->lock);
+ return ret;
+}
+
+void cw1200_queue_lock(struct cw1200_queue *queue)
+{
+ spin_lock_bh(&queue->lock);
+ __cw1200_queue_lock(queue);
+ spin_unlock_bh(&queue->lock);
+}
+
+void cw1200_queue_unlock(struct cw1200_queue *queue)
+{
+ spin_lock_bh(&queue->lock);
+ __cw1200_queue_unlock(queue);
+ spin_unlock_bh(&queue->lock);
+}
+
+bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
+ unsigned long *timestamp,
+ u32 pending_frame_id)
+{
+ struct cw1200_queue_item *item;
+ bool ret;
+
+ spin_lock_bh(&queue->lock);
+ ret = !list_empty(&queue->pending);
+ if (ret) {
+ list_for_each_entry(item, &queue->pending, head) {
+ if (item->packet_id != pending_frame_id)
+ if (time_before(item->xmit_timestamp,
+ *timestamp))
+ *timestamp = item->xmit_timestamp;
+ }
+ }
+ spin_unlock_bh(&queue->lock);
+ return ret;
+}
+
+bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
+ u32 link_id_map)
+{
+ bool empty = true;
+
+ spin_lock_bh(&stats->lock);
+ if (link_id_map == (u32)-1) {
+ empty = stats->num_queued == 0;
+ } else {
+ int i;
+ for (i = 0; i < stats->map_capacity; ++i) {
+ if (link_id_map & BIT(i)) {
+ if (stats->link_map_cache[i]) {
+ empty = false;
+ break;
+ }
+ }
+ }
+ }
+ spin_unlock_bh(&stats->lock);
+
+ return empty;
+}
diff --git a/drivers/net/wireless/cw1200/queue.h b/drivers/net/wireless/cw1200/queue.h
new file mode 100644
index 000000000000..119f9c79c14e
--- /dev/null
+++ b/drivers/net/wireless/cw1200/queue.h
@@ -0,0 +1,116 @@
+/*
+ * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 CW1200_QUEUE_H_INCLUDED
+#define CW1200_QUEUE_H_INCLUDED
+
+/* private */ struct cw1200_queue_item;
+
+/* extern */ struct sk_buff;
+/* extern */ struct wsm_tx;
+/* extern */ struct cw1200_common;
+/* extern */ struct ieee80211_tx_queue_stats;
+/* extern */ struct cw1200_txpriv;
+
+/* forward */ struct cw1200_queue_stats;
+
+typedef void (*cw1200_queue_skb_dtor_t)(struct cw1200_common *priv,
+ struct sk_buff *skb,
+ const struct cw1200_txpriv *txpriv);
+
+struct cw1200_queue {
+ struct cw1200_queue_stats *stats;
+ size_t capacity;
+ size_t num_queued;
+ size_t num_pending;
+ size_t num_sent;
+ struct cw1200_queue_item *pool;
+ struct list_head queue;
+ struct list_head free_pool;
+ struct list_head pending;
+ int tx_locked_cnt;
+ int *link_map_cache;
+ bool overfull;
+ spinlock_t lock; /* Protect queue entry */
+ u8 queue_id;
+ u8 generation;
+ struct timer_list gc;
+ unsigned long ttl;
+};
+
+struct cw1200_queue_stats {
+ spinlock_t lock; /* Protect stats entry */
+ int *link_map_cache;
+ int num_queued;
+ size_t map_capacity;
+ wait_queue_head_t wait_link_id_empty;
+ cw1200_queue_skb_dtor_t skb_dtor;
+ struct cw1200_common *priv;
+};
+
+struct cw1200_txpriv {
+ u8 link_id;
+ u8 raw_link_id;
+ u8 tid;
+ u8 rate_id;
+ u8 offset;
+};
+
+int cw1200_queue_stats_init(struct cw1200_queue_stats *stats,
+ size_t map_capacity,
+ cw1200_queue_skb_dtor_t skb_dtor,
+ struct cw1200_common *priv);
+int cw1200_queue_init(struct cw1200_queue *queue,
+ struct cw1200_queue_stats *stats,
+ u8 queue_id,
+ size_t capacity,
+ unsigned long ttl);
+int cw1200_queue_clear(struct cw1200_queue *queue);
+void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats);
+void cw1200_queue_deinit(struct cw1200_queue *queue);
+
+size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue,
+ u32 link_id_map);
+int cw1200_queue_put(struct cw1200_queue *queue,
+ struct sk_buff *skb,
+ struct cw1200_txpriv *txpriv);
+int cw1200_queue_get(struct cw1200_queue *queue,
+ u32 link_id_map,
+ struct wsm_tx **tx,
+ struct ieee80211_tx_info **tx_info,
+ const struct cw1200_txpriv **txpriv);
+int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id);
+int cw1200_queue_requeue_all(struct cw1200_queue *queue);
+int cw1200_queue_remove(struct cw1200_queue *queue,
+ u32 packet_id);
+int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id,
+ struct sk_buff **skb,
+ const struct cw1200_txpriv **txpriv);
+void cw1200_queue_lock(struct cw1200_queue *queue);
+void cw1200_queue_unlock(struct cw1200_queue *queue);
+bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
+ unsigned long *timestamp,
+ u32 pending_frame_id);
+
+bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
+ u32 link_id_map);
+
+static inline u8 cw1200_queue_get_queue_id(u32 packet_id)
+{
+ return (packet_id >> 16) & 0xFF;
+}
+
+static inline u8 cw1200_queue_get_generation(u32 packet_id)
+{
+ return (packet_id >> 8) & 0xFF;
+}
+
+#endif /* CW1200_QUEUE_H_INCLUDED */
diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c
new file mode 100644
index 000000000000..ee3c19037aac
--- /dev/null
+++ b/drivers/net/wireless/cw1200/scan.c
@@ -0,0 +1,461 @@
+/*
+ * Scan implementation for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/sched.h>
+#include "cw1200.h"
+#include "scan.h"
+#include "sta.h"
+#include "pm.h"
+
+static void cw1200_scan_restart_delayed(struct cw1200_common *priv);
+
+static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan)
+{
+ int ret, i;
+ int tmo = 2000;
+
+ switch (priv->join_status) {
+ case CW1200_JOIN_STATUS_PRE_STA:
+ case CW1200_JOIN_STATUS_JOINING:
+ return -EBUSY;
+ default:
+ break;
+ }
+
+ wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n",
+ scan->type, scan->num_channels, scan->flags);
+
+ for (i = 0; i < scan->num_channels; ++i)
+ tmo += scan->ch[i].max_chan_time + 10;
+
+ cancel_delayed_work_sync(&priv->clear_recent_scan_work);
+ atomic_set(&priv->scan.in_progress, 1);
+ atomic_set(&priv->recent_scan, 1);
+ cw1200_pm_stay_awake(&priv->pm_state, tmo * HZ / 1000);
+ queue_delayed_work(priv->workqueue, &priv->scan.timeout,
+ tmo * HZ / 1000);
+ ret = wsm_scan(priv, scan);
+ if (ret) {
+ atomic_set(&priv->scan.in_progress, 0);
+ cancel_delayed_work_sync(&priv->scan.timeout);
+ cw1200_scan_restart_delayed(priv);
+ }
+ return ret;
+}
+
+int cw1200_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_scan_request *req)
+{
+ struct cw1200_common *priv = hw->priv;
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
+ };
+ int i, ret;
+
+ if (!priv->vif)
+ return -EINVAL;
+
+ /* Scan when P2P_GO corrupt firmware MiniAP mode */
+ if (priv->join_status == CW1200_JOIN_STATUS_AP)
+ return -EOPNOTSUPP;
+
+ if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
+ req->n_ssids = 0;
+
+ wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n",
+ req->n_ssids);
+
+ if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS)
+ return -EINVAL;
+
+ frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0,
+ req->ie_len);
+ if (!frame.skb)
+ return -ENOMEM;
+
+ if (req->ie_len)
+ memcpy(skb_put(frame.skb, req->ie_len), req->ie, req->ie_len);
+
+ /* will be unlocked in cw1200_scan_work() */
+ down(&priv->scan.lock);
+ mutex_lock(&priv->conf_mutex);
+
+ ret = wsm_set_template_frame(priv, &frame);
+ if (!ret) {
+ /* Host want to be the probe responder. */
+ ret = wsm_set_probe_responder(priv, true);
+ }
+ if (ret) {
+ mutex_unlock(&priv->conf_mutex);
+ up(&priv->scan.lock);
+ dev_kfree_skb(frame.skb);
+ return ret;
+ }
+
+ wsm_lock_tx(priv);
+
+ BUG_ON(priv->scan.req);
+ priv->scan.req = req;
+ priv->scan.n_ssids = 0;
+ priv->scan.status = 0;
+ priv->scan.begin = &req->channels[0];
+ priv->scan.curr = priv->scan.begin;
+ priv->scan.end = &req->channels[req->n_channels];
+ priv->scan.output_power = priv->output_power;
+
+ for (i = 0; i < req->n_ssids; ++i) {
+ struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids];
+ memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid));
+ dst->length = req->ssids[i].ssid_len;
+ ++priv->scan.n_ssids;
+ }
+
+ mutex_unlock(&priv->conf_mutex);
+
+ if (frame.skb)
+ dev_kfree_skb(frame.skb);
+ queue_work(priv->workqueue, &priv->scan.work);
+ return 0;
+}
+
+void cw1200_scan_work(struct work_struct *work)
+{
+ struct cw1200_common *priv = container_of(work, struct cw1200_common,
+ scan.work);
+ struct ieee80211_channel **it;
+ struct wsm_scan scan = {
+ .type = WSM_SCAN_TYPE_FOREGROUND,
+ .flags = WSM_SCAN_FLAG_SPLIT_METHOD,
+ };
+ bool first_run = (priv->scan.begin == priv->scan.curr &&
+ priv->scan.begin != priv->scan.end);
+ int i;
+
+ if (first_run) {
+ /* Firmware gets crazy if scan request is sent
+ * when STA is joined but not yet associated.
+ * Force unjoin in this case.
+ */
+ if (cancel_delayed_work_sync(&priv->join_timeout) > 0)
+ cw1200_join_timeout(&priv->join_timeout.work);
+ }
+
+ mutex_lock(&priv->conf_mutex);
+
+ if (first_run) {
+ if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+ !(priv->powersave_mode.mode & WSM_PSM_PS)) {
+ struct wsm_set_pm pm = priv->powersave_mode;
+ pm.mode = WSM_PSM_PS;
+ cw1200_set_pm(priv, &pm);
+ } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
+ /* FW bug: driver has to restart p2p-dev mode
+ * after scan
+ */
+ cw1200_disable_listening(priv);
+ }
+ }
+
+ if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) {
+ if (priv->scan.output_power != priv->output_power)
+ wsm_set_output_power(priv, priv->output_power * 10);
+ if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+ !(priv->powersave_mode.mode & WSM_PSM_PS))
+ cw1200_set_pm(priv, &priv->powersave_mode);
+
+ if (priv->scan.status < 0)
+ wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan failed (%d).\n",
+ priv->scan.status);
+ else if (priv->scan.req)
+ wiphy_dbg(priv->hw->wiphy,
+ "[SCAN] Scan completed.\n");
+ else
+ wiphy_dbg(priv->hw->wiphy,
+ "[SCAN] Scan canceled.\n");
+
+ priv->scan.req = NULL;
+ cw1200_scan_restart_delayed(priv);
+ wsm_unlock_tx(priv);
+ mutex_unlock(&priv->conf_mutex);
+ ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0);
+ up(&priv->scan.lock);
+ return;
+ } else {
+ struct ieee80211_channel *first = *priv->scan.curr;
+ for (it = priv->scan.curr + 1, i = 1;
+ it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS;
+ ++it, ++i) {
+ if ((*it)->band != first->band)
+ break;
+ if (((*it)->flags ^ first->flags) &
+ IEEE80211_CHAN_PASSIVE_SCAN)
+ break;
+ if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
+ (*it)->max_power != first->max_power)
+ break;
+ }
+ scan.band = first->band;
+
+ if (priv->scan.req->no_cck)
+ scan.max_tx_rate = WSM_TRANSMIT_RATE_6;
+ else
+ scan.max_tx_rate = WSM_TRANSMIT_RATE_1;
+ scan.num_probes =
+ (first->flags & IEEE80211_CHAN_PASSIVE_SCAN) ? 0 : 2;
+ scan.num_ssids = priv->scan.n_ssids;
+ scan.ssids = &priv->scan.ssids[0];
+ scan.num_channels = it - priv->scan.curr;
+ /* TODO: Is it optimal? */
+ scan.probe_delay = 100;
+ /* It is not stated in WSM specification, however
+ * FW team says that driver may not use FG scan
+ * when joined.
+ */
+ if (priv->join_status == CW1200_JOIN_STATUS_STA) {
+ scan.type = WSM_SCAN_TYPE_BACKGROUND;
+ scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
+ }
+ scan.ch = kzalloc(
+ sizeof(struct wsm_scan_ch) * (it - priv->scan.curr),
+ GFP_KERNEL);
+ if (!scan.ch) {
+ priv->scan.status = -ENOMEM;
+ goto fail;
+ }
+ for (i = 0; i < scan.num_channels; ++i) {
+ scan.ch[i].number = priv->scan.curr[i]->hw_value;
+ if (priv->scan.curr[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN) {
+ scan.ch[i].min_chan_time = 50;
+ scan.ch[i].max_chan_time = 100;
+ } else {
+ scan.ch[i].min_chan_time = 10;
+ scan.ch[i].max_chan_time = 25;
+ }
+ }
+ if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
+ priv->scan.output_power != first->max_power) {
+ priv->scan.output_power = first->max_power;
+ wsm_set_output_power(priv,
+ priv->scan.output_power * 10);
+ }
+ priv->scan.status = cw1200_scan_start(priv, &scan);
+ kfree(scan.ch);
+ if (priv->scan.status)
+ goto fail;
+ priv->scan.curr = it;
+ }
+ mutex_unlock(&priv->conf_mutex);
+ return;
+
+fail:
+ priv->scan.curr = priv->scan.end;
+ mutex_unlock(&priv->conf_mutex);
+ queue_work(priv->workqueue, &priv->scan.work);
+ return;
+}
+
+static void cw1200_scan_restart_delayed(struct cw1200_common *priv)
+{
+ /* FW bug: driver has to restart p2p-dev mode after scan. */
+ if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
+ cw1200_enable_listening(priv);
+ cw1200_update_filtering(priv);
+ }
+
+ if (priv->delayed_unjoin) {
+ priv->delayed_unjoin = false;
+ if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+ } else if (priv->delayed_link_loss) {
+ wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n");
+ priv->delayed_link_loss = 0;
+ cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
+ }
+}
+
+static void cw1200_scan_complete(struct cw1200_common *priv)
+{
+ queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ);
+ if (priv->scan.direct_probe) {
+ wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n");
+ cw1200_scan_restart_delayed(priv);
+ priv->scan.direct_probe = 0;
+ up(&priv->scan.lock);
+ wsm_unlock_tx(priv);
+ } else {
+ cw1200_scan_work(&priv->scan.work);
+ }
+}
+
+void cw1200_scan_failed_cb(struct cw1200_common *priv)
+{
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+ /* STA is stopped. */
+ return;
+
+ if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
+ priv->scan.status = -EIO;
+ queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
+ }
+}
+
+
+void cw1200_scan_complete_cb(struct cw1200_common *priv,
+ struct wsm_scan_complete *arg)
+{
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+ /* STA is stopped. */
+ return;
+
+ if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
+ priv->scan.status = 1;
+ queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
+ }
+}
+
+void cw1200_clear_recent_scan_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common,
+ clear_recent_scan_work.work);
+ atomic_xchg(&priv->recent_scan, 0);
+}
+
+void cw1200_scan_timeout(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, scan.timeout.work);
+ if (atomic_xchg(&priv->scan.in_progress, 0)) {
+ if (priv->scan.status > 0) {
+ priv->scan.status = 0;
+ } else if (!priv->scan.status) {
+ wiphy_warn(priv->hw->wiphy,
+ "Timeout waiting for scan complete notification.\n");
+ priv->scan.status = -ETIMEDOUT;
+ priv->scan.curr = priv->scan.end;
+ wsm_stop_scan(priv);
+ }
+ cw1200_scan_complete(priv);
+ }
+}
+
+void cw1200_probe_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, scan.probe_work.work);
+ u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
+ struct cw1200_queue *queue = &priv->tx_queue[queue_id];
+ const struct cw1200_txpriv *txpriv;
+ struct wsm_tx *wsm;
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
+ };
+ struct wsm_ssid ssids[1] = {{
+ .length = 0,
+ } };
+ struct wsm_scan_ch ch[1] = {{
+ .min_chan_time = 0,
+ .max_chan_time = 10,
+ } };
+ struct wsm_scan scan = {
+ .type = WSM_SCAN_TYPE_FOREGROUND,
+ .num_probes = 1,
+ .probe_delay = 0,
+ .num_channels = 1,
+ .ssids = ssids,
+ .ch = ch,
+ };
+ u8 *ies;
+ size_t ies_len;
+ int ret;
+
+ wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n");
+
+ mutex_lock(&priv->conf_mutex);
+ if (down_trylock(&priv->scan.lock)) {
+ /* Scan is already in progress. Requeue self. */
+ schedule();
+ queue_delayed_work(priv->workqueue,
+ &priv->scan.probe_work, HZ / 10);
+ mutex_unlock(&priv->conf_mutex);
+ return;
+ }
+
+ /* Make sure we still have a pending probe req */
+ if (cw1200_queue_get_skb(queue, priv->pending_frame_id,
+ &frame.skb, &txpriv)) {
+ up(&priv->scan.lock);
+ mutex_unlock(&priv->conf_mutex);
+ wsm_unlock_tx(priv);
+ return;
+ }
+ wsm = (struct wsm_tx *)frame.skb->data;
+ scan.max_tx_rate = wsm->max_tx_rate;
+ scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
+ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+ if (priv->join_status == CW1200_JOIN_STATUS_STA ||
+ priv->join_status == CW1200_JOIN_STATUS_IBSS) {
+ scan.type = WSM_SCAN_TYPE_BACKGROUND;
+ scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
+ }
+ ch[0].number = priv->channel->hw_value;
+
+ skb_pull(frame.skb, txpriv->offset);
+
+ ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)];
+ ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr);
+
+ if (ies_len) {
+ u8 *ssidie =
+ (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len);
+ if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) {
+ u8 *nextie = &ssidie[2 + ssidie[1]];
+ /* Remove SSID from the IE list. It has to be provided
+ * as a separate argument in cw1200_scan_start call
+ */
+
+ /* Store SSID localy */
+ ssids[0].length = ssidie[1];
+ memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length);
+ scan.num_ssids = 1;
+
+ /* Remove SSID from IE list */
+ ssidie[1] = 0;
+ memmove(&ssidie[2], nextie, &ies[ies_len] - nextie);
+ skb_trim(frame.skb, frame.skb->len - ssids[0].length);
+ }
+ }
+
+ /* FW bug: driver has to restart p2p-dev mode after scan */
+ if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
+ cw1200_disable_listening(priv);
+ ret = wsm_set_template_frame(priv, &frame);
+ priv->scan.direct_probe = 1;
+ if (!ret) {
+ wsm_flush_tx(priv);
+ ret = cw1200_scan_start(priv, &scan);
+ }
+ mutex_unlock(&priv->conf_mutex);
+
+ skb_push(frame.skb, txpriv->offset);
+ if (!ret)
+ IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK;
+ BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id));
+
+ if (ret) {
+ priv->scan.direct_probe = 0;
+ up(&priv->scan.lock);
+ wsm_unlock_tx(priv);
+ }
+
+ return;
+}
diff --git a/drivers/net/wireless/cw1200/scan.h b/drivers/net/wireless/cw1200/scan.h
new file mode 100644
index 000000000000..5a8296ccfa82
--- /dev/null
+++ b/drivers/net/wireless/cw1200/scan.h
@@ -0,0 +1,56 @@
+/*
+ * Scan interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 SCAN_H_INCLUDED
+#define SCAN_H_INCLUDED
+
+#include <linux/semaphore.h>
+#include "wsm.h"
+
+/* external */ struct sk_buff;
+/* external */ struct cfg80211_scan_request;
+/* external */ struct ieee80211_channel;
+/* external */ struct ieee80211_hw;
+/* external */ struct work_struct;
+
+struct cw1200_scan {
+ struct semaphore lock;
+ struct work_struct work;
+ struct delayed_work timeout;
+ struct cfg80211_scan_request *req;
+ struct ieee80211_channel **begin;
+ struct ieee80211_channel **curr;
+ struct ieee80211_channel **end;
+ struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS];
+ int output_power;
+ int n_ssids;
+ int status;
+ atomic_t in_progress;
+ /* Direct probe requests workaround */
+ struct delayed_work probe_work;
+ int direct_probe;
+};
+
+int cw1200_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_scan_request *req);
+void cw1200_scan_work(struct work_struct *work);
+void cw1200_scan_timeout(struct work_struct *work);
+void cw1200_clear_recent_scan_work(struct work_struct *work);
+void cw1200_scan_complete_cb(struct cw1200_common *priv,
+ struct wsm_scan_complete *arg);
+void cw1200_scan_failed_cb(struct cw1200_common *priv);
+
+/* ******************************************************************** */
+/* Raw probe requests TX workaround */
+void cw1200_probe_work(struct work_struct *work);
+
+#endif
diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c
new file mode 100644
index 000000000000..4cd0352b508d
--- /dev/null
+++ b/drivers/net/wireless/cw1200/sta.c
@@ -0,0 +1,2404 @@
+/*
+ * Mac80211 STA API for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+
+#include "cw1200.h"
+#include "sta.h"
+#include "fwio.h"
+#include "bh.h"
+#include "debug.h"
+
+#ifndef ERP_INFO_BYTE_OFFSET
+#define ERP_INFO_BYTE_OFFSET 2
+#endif
+
+static void cw1200_do_join(struct cw1200_common *priv);
+static void cw1200_do_unjoin(struct cw1200_common *priv);
+
+static int cw1200_upload_beacon(struct cw1200_common *priv);
+static int cw1200_upload_pspoll(struct cw1200_common *priv);
+static int cw1200_upload_null(struct cw1200_common *priv);
+static int cw1200_upload_qosnull(struct cw1200_common *priv);
+static int cw1200_start_ap(struct cw1200_common *priv);
+static int cw1200_update_beaconing(struct cw1200_common *priv);
+static int cw1200_enable_beaconing(struct cw1200_common *priv,
+ bool enable);
+static void __cw1200_sta_notify(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd notify_cmd,
+ int link_id);
+static int __cw1200_flush(struct cw1200_common *priv, bool drop);
+
+static inline void __cw1200_free_event_queue(struct list_head *list)
+{
+ struct cw1200_wsm_event *event, *tmp;
+ list_for_each_entry_safe(event, tmp, list, link) {
+ list_del(&event->link);
+ kfree(event);
+ }
+}
+
+/* ******************************************************************** */
+/* STA API */
+
+int cw1200_start(struct ieee80211_hw *dev)
+{
+ struct cw1200_common *priv = dev->priv;
+ int ret = 0;
+
+ cw1200_pm_stay_awake(&priv->pm_state, HZ);
+
+ mutex_lock(&priv->conf_mutex);
+
+ /* default EDCA */
+ WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, 0xc8, false);
+ WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, 0xc8, false);
+ WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, 0xc8, false);
+ WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, 0xc8, false);
+ ret = wsm_set_edca_params(priv, &priv->edca);
+ if (ret)
+ goto out;
+
+ ret = cw1200_set_uapsd_param(priv, &priv->edca);
+ if (ret)
+ goto out;
+
+ priv->setbssparams_done = false;
+
+ memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN);
+ priv->mode = NL80211_IFTYPE_MONITOR;
+ priv->wep_default_key_id = -1;
+
+ priv->cqm_beacon_loss_count = 10;
+
+ ret = cw1200_setup_mac(priv);
+ if (ret)
+ goto out;
+
+out:
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+void cw1200_stop(struct ieee80211_hw *dev)
+{
+ struct cw1200_common *priv = dev->priv;
+ LIST_HEAD(list);
+ int i;
+
+ wsm_lock_tx(priv);
+
+ while (down_trylock(&priv->scan.lock)) {
+ /* Scan is in progress. Force it to stop. */
+ priv->scan.req = NULL;
+ schedule();
+ }
+ up(&priv->scan.lock);
+
+ cancel_delayed_work_sync(&priv->scan.probe_work);
+ cancel_delayed_work_sync(&priv->scan.timeout);
+ cancel_delayed_work_sync(&priv->clear_recent_scan_work);
+ cancel_delayed_work_sync(&priv->join_timeout);
+ cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+ cancel_work_sync(&priv->unjoin_work);
+ cancel_delayed_work_sync(&priv->link_id_gc_work);
+ flush_workqueue(priv->workqueue);
+ del_timer_sync(&priv->mcast_timeout);
+ mutex_lock(&priv->conf_mutex);
+ priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->listening = false;
+
+ spin_lock(&priv->event_queue_lock);
+ list_splice_init(&priv->event_queue, &list);
+ spin_unlock(&priv->event_queue_lock);
+ __cw1200_free_event_queue(&list);
+
+
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ priv->join_pending = false;
+
+ for (i = 0; i < 4; i++)
+ cw1200_queue_clear(&priv->tx_queue[i]);
+ mutex_unlock(&priv->conf_mutex);
+ tx_policy_clean(priv);
+
+ /* HACK! */
+ if (atomic_xchg(&priv->tx_lock, 1) != 1)
+ pr_debug("[STA] TX is force-unlocked due to stop request.\n");
+
+ wsm_unlock_tx(priv);
+ atomic_xchg(&priv->tx_lock, 0); /* for recovery to work */
+}
+
+static int cw1200_bssloss_mitigation = 1;
+module_param(cw1200_bssloss_mitigation, int, 0644);
+MODULE_PARM_DESC(cw1200_bssloss_mitigation, "BSS Loss mitigation. 0 == disabled, 1 == enabled (default)");
+
+
+void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv,
+ int init, int good, int bad)
+{
+ int tx = 0;
+
+ priv->delayed_link_loss = 0;
+ cancel_work_sync(&priv->bss_params_work);
+
+ pr_debug("[STA] CQM BSSLOSS_SM: state: %d init %d good %d bad: %d txlock: %d uj: %d\n",
+ priv->bss_loss_state,
+ init, good, bad,
+ atomic_read(&priv->tx_lock),
+ priv->delayed_unjoin);
+
+ /* If we have a pending unjoin */
+ if (priv->delayed_unjoin)
+ return;
+
+ if (init) {
+ queue_delayed_work(priv->workqueue,
+ &priv->bss_loss_work,
+ HZ);
+ priv->bss_loss_state = 0;
+
+ /* Skip the confimration procedure in P2P case */
+ if (!priv->vif->p2p && !atomic_read(&priv->tx_lock))
+ tx = 1;
+ } else if (good) {
+ cancel_delayed_work_sync(&priv->bss_loss_work);
+ priv->bss_loss_state = 0;
+ queue_work(priv->workqueue, &priv->bss_params_work);
+ } else if (bad) {
+ /* XXX Should we just keep going until we time out? */
+ if (priv->bss_loss_state < 3)
+ tx = 1;
+ } else {
+ cancel_delayed_work_sync(&priv->bss_loss_work);
+ priv->bss_loss_state = 0;
+ }
+
+ /* Bypass mitigation if it's disabled */
+ if (!cw1200_bssloss_mitigation)
+ tx = 0;
+
+ /* Spit out a NULL packet to our AP if necessary */
+ if (tx) {
+ struct sk_buff *skb;
+
+ priv->bss_loss_state++;
+
+ skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
+ WARN_ON(!skb);
+ if (skb)
+ cw1200_tx(priv->hw, NULL, skb);
+ }
+}
+
+int cw1200_add_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
+{
+ int ret;
+ struct cw1200_common *priv = dev->priv;
+ /* __le32 auto_calibration_mode = __cpu_to_le32(1); */
+
+ vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+ IEEE80211_VIF_SUPPORTS_CQM_RSSI;
+
+ mutex_lock(&priv->conf_mutex);
+
+ if (priv->mode != NL80211_IFTYPE_MONITOR) {
+ mutex_unlock(&priv->conf_mutex);
+ return -EOPNOTSUPP;
+ }
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_AP:
+ priv->mode = vif->type;
+ break;
+ default:
+ mutex_unlock(&priv->conf_mutex);
+ return -EOPNOTSUPP;
+ }
+
+ priv->vif = vif;
+ memcpy(priv->mac_addr, vif->addr, ETH_ALEN);
+ ret = cw1200_setup_mac(priv);
+ /* Enable auto-calibration */
+ /* Exception in subsequent channel switch; disabled.
+ * wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE,
+ * &auto_calibration_mode, sizeof(auto_calibration_mode));
+ */
+
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+void cw1200_remove_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
+{
+ struct cw1200_common *priv = dev->priv;
+ struct wsm_reset reset = {
+ .reset_statistics = true,
+ };
+ int i;
+
+ mutex_lock(&priv->conf_mutex);
+ switch (priv->join_status) {
+ case CW1200_JOIN_STATUS_JOINING:
+ case CW1200_JOIN_STATUS_PRE_STA:
+ case CW1200_JOIN_STATUS_STA:
+ case CW1200_JOIN_STATUS_IBSS:
+ wsm_lock_tx(priv);
+ if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+ break;
+ case CW1200_JOIN_STATUS_AP:
+ for (i = 0; priv->link_id_map; ++i) {
+ if (priv->link_id_map & BIT(i)) {
+ reset.link_id = i;
+ wsm_reset(priv, &reset);
+ priv->link_id_map &= ~BIT(i);
+ }
+ }
+ memset(priv->link_id_db, 0, sizeof(priv->link_id_db));
+ priv->sta_asleep_mask = 0;
+ priv->enable_beacon = false;
+ priv->tx_multicast = false;
+ priv->aid0_bit_set = false;
+ priv->buffered_multicasts = false;
+ priv->pspoll_mask = 0;
+ reset.link_id = 0;
+ wsm_reset(priv, &reset);
+ break;
+ case CW1200_JOIN_STATUS_MONITOR:
+ cw1200_update_listening(priv, false);
+ break;
+ default:
+ break;
+ }
+ priv->vif = NULL;
+ priv->mode = NL80211_IFTYPE_MONITOR;
+ memset(priv->mac_addr, 0, ETH_ALEN);
+ memset(&priv->p2p_ps_modeinfo, 0, sizeof(priv->p2p_ps_modeinfo));
+ cw1200_free_keys(priv);
+ cw1200_setup_mac(priv);
+ priv->listening = false;
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ if (!__cw1200_flush(priv, true))
+ wsm_unlock_tx(priv);
+
+ mutex_unlock(&priv->conf_mutex);
+}
+
+int cw1200_change_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype new_type,
+ bool p2p)
+{
+ int ret = 0;
+ pr_debug("change_interface new: %d (%d), old: %d (%d)\n", new_type,
+ p2p, vif->type, vif->p2p);
+
+ if (new_type != vif->type || vif->p2p != p2p) {
+ cw1200_remove_interface(dev, vif);
+ vif->type = new_type;
+ vif->p2p = p2p;
+ ret = cw1200_add_interface(dev, vif);
+ }
+
+ return ret;
+}
+
+int cw1200_config(struct ieee80211_hw *dev, u32 changed)
+{
+ int ret = 0;
+ struct cw1200_common *priv = dev->priv;
+ struct ieee80211_conf *conf = &dev->conf;
+
+ pr_debug("CONFIG CHANGED: %08x\n", changed);
+
+ down(&priv->scan.lock);
+ mutex_lock(&priv->conf_mutex);
+ /* TODO: IEEE80211_CONF_CHANGE_QOS */
+ /* TODO: IEEE80211_CONF_CHANGE_LISTEN_INTERVAL */
+
+ if (changed & IEEE80211_CONF_CHANGE_POWER) {
+ priv->output_power = conf->power_level;
+ pr_debug("[STA] TX power: %d\n", priv->output_power);
+ wsm_set_output_power(priv, priv->output_power * 10);
+ }
+
+ if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) &&
+ (priv->channel != conf->chandef.chan)) {
+ struct ieee80211_channel *ch = conf->chandef.chan;
+ struct wsm_switch_channel channel = {
+ .channel_number = ch->hw_value,
+ };
+ pr_debug("[STA] Freq %d (wsm ch: %d).\n",
+ ch->center_freq, ch->hw_value);
+
+ /* __cw1200_flush() implicitly locks tx, if successful */
+ if (!__cw1200_flush(priv, false)) {
+ if (!wsm_switch_channel(priv, &channel)) {
+ ret = wait_event_timeout(priv->channel_switch_done,
+ !priv->channel_switch_in_progress,
+ 3 * HZ);
+ if (ret) {
+ /* Already unlocks if successful */
+ priv->channel = ch;
+ ret = 0;
+ } else {
+ ret = -ETIMEDOUT;
+ }
+ } else {
+ /* Unlock if switch channel fails */
+ wsm_unlock_tx(priv);
+ }
+ }
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_PS) {
+ if (!(conf->flags & IEEE80211_CONF_PS))
+ priv->powersave_mode.mode = WSM_PSM_ACTIVE;
+ else if (conf->dynamic_ps_timeout <= 0)
+ priv->powersave_mode.mode = WSM_PSM_PS;
+ else
+ priv->powersave_mode.mode = WSM_PSM_FAST_PS;
+
+ /* Firmware requires that value for this 1-byte field must
+ * be specified in units of 500us. Values above the 128ms
+ * threshold are not supported.
+ */
+ if (conf->dynamic_ps_timeout >= 0x80)
+ priv->powersave_mode.fast_psm_idle_period = 0xFF;
+ else
+ priv->powersave_mode.fast_psm_idle_period =
+ conf->dynamic_ps_timeout << 1;
+
+ if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+ priv->bss_params.aid)
+ cw1200_set_pm(priv, &priv->powersave_mode);
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+ /* TBD: It looks like it's transparent
+ * there's a monitor interface present -- use this
+ * to determine for example whether to calculate
+ * timestamps for packets or not, do not use instead
+ * of filter flags!
+ */
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_IDLE) {
+ struct wsm_operational_mode mode = {
+ .power_mode = cw1200_power_mode,
+ .disable_more_flag_usage = true,
+ };
+
+ wsm_lock_tx(priv);
+ /* Disable p2p-dev mode forced by TX request */
+ if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) &&
+ (conf->flags & IEEE80211_CONF_IDLE) &&
+ !priv->listening) {
+ cw1200_disable_listening(priv);
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ }
+ wsm_set_operational_mode(priv, &mode);
+ wsm_unlock_tx(priv);
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
+ pr_debug("[STA] Retry limits: %d (long), %d (short).\n",
+ conf->long_frame_max_tx_count,
+ conf->short_frame_max_tx_count);
+ spin_lock_bh(&priv->tx_policy_cache.lock);
+ priv->long_frame_max_tx_count = conf->long_frame_max_tx_count;
+ priv->short_frame_max_tx_count =
+ (conf->short_frame_max_tx_count < 0x0F) ?
+ conf->short_frame_max_tx_count : 0x0F;
+ priv->hw->max_rate_tries = priv->short_frame_max_tx_count;
+ spin_unlock_bh(&priv->tx_policy_cache.lock);
+ }
+ mutex_unlock(&priv->conf_mutex);
+ up(&priv->scan.lock);
+ return ret;
+}
+
+void cw1200_update_filtering(struct cw1200_common *priv)
+{
+ int ret;
+ bool bssid_filtering = !priv->rx_filter.bssid;
+ bool is_p2p = priv->vif && priv->vif->p2p;
+ bool is_sta = priv->vif && NL80211_IFTYPE_STATION == priv->vif->type;
+
+ static struct wsm_beacon_filter_control bf_ctrl;
+ static struct wsm_mib_beacon_filter_table bf_tbl = {
+ .entry[0].ie_id = WLAN_EID_VENDOR_SPECIFIC,
+ .entry[0].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+ WSM_BEACON_FILTER_IE_HAS_APPEARED,
+ .entry[0].oui[0] = 0x50,
+ .entry[0].oui[1] = 0x6F,
+ .entry[0].oui[2] = 0x9A,
+ .entry[1].ie_id = WLAN_EID_HT_OPERATION,
+ .entry[1].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+ WSM_BEACON_FILTER_IE_HAS_APPEARED,
+ .entry[2].ie_id = WLAN_EID_ERP_INFO,
+ .entry[2].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+ WSM_BEACON_FILTER_IE_HAS_APPEARED,
+ };
+
+ if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE)
+ return;
+ else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
+ bssid_filtering = false;
+
+ if (priv->disable_beacon_filter) {
+ bf_ctrl.enabled = 0;
+ bf_ctrl.bcn_count = 1;
+ bf_tbl.num = __cpu_to_le32(0);
+ } else if (is_p2p || !is_sta) {
+ bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE |
+ WSM_BEACON_FILTER_AUTO_ERP;
+ bf_ctrl.bcn_count = 0;
+ bf_tbl.num = __cpu_to_le32(2);
+ } else {
+ bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE;
+ bf_ctrl.bcn_count = 0;
+ bf_tbl.num = __cpu_to_le32(3);
+ }
+
+ /* When acting as p2p client being connected to p2p GO, in order to
+ * receive frames from a different p2p device, turn off bssid filter.
+ *
+ * WARNING: FW dependency!
+ * This can only be used with FW WSM371 and its successors.
+ * In that FW version even with bssid filter turned off,
+ * device will block most of the unwanted frames.
+ */
+ if (is_p2p)
+ bssid_filtering = false;
+
+ ret = wsm_set_rx_filter(priv, &priv->rx_filter);
+ if (!ret)
+ ret = wsm_set_beacon_filter_table(priv, &bf_tbl);
+ if (!ret)
+ ret = wsm_beacon_filter_control(priv, &bf_ctrl);
+ if (!ret)
+ ret = wsm_set_bssid_filtering(priv, bssid_filtering);
+ if (!ret)
+ ret = wsm_set_multicast_filter(priv, &priv->multicast_filter);
+ if (ret)
+ wiphy_err(priv->hw->wiphy,
+ "Update filtering failed: %d.\n", ret);
+ return;
+}
+
+void cw1200_update_filtering_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common,
+ update_filtering_work);
+
+ cw1200_update_filtering(priv);
+}
+
+void cw1200_set_beacon_wakeup_period_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common,
+ set_beacon_wakeup_period_work);
+
+ wsm_set_beacon_wakeup_period(priv,
+ priv->beacon_int * priv->join_dtim_period >
+ MAX_BEACON_SKIP_TIME_MS ? 1 :
+ priv->join_dtim_period, 0);
+}
+
+u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
+ struct netdev_hw_addr_list *mc_list)
+{
+ static u8 broadcast_ipv6[ETH_ALEN] = {
+ 0x33, 0x33, 0x00, 0x00, 0x00, 0x01
+ };
+ static u8 broadcast_ipv4[ETH_ALEN] = {
+ 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01
+ };
+ struct cw1200_common *priv = hw->priv;
+ struct netdev_hw_addr *ha;
+ int count = 0;
+
+ /* Disable multicast filtering */
+ priv->has_multicast_subscription = false;
+ memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter));
+
+ if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES)
+ return 0;
+
+ /* Enable if requested */
+ netdev_hw_addr_list_for_each(ha, mc_list) {
+ pr_debug("[STA] multicast: %pM\n", ha->addr);
+ memcpy(&priv->multicast_filter.macaddrs[count],
+ ha->addr, ETH_ALEN);
+ if (memcmp(ha->addr, broadcast_ipv4, ETH_ALEN) &&
+ memcmp(ha->addr, broadcast_ipv6, ETH_ALEN))
+ priv->has_multicast_subscription = true;
+ count++;
+ }
+
+ if (count) {
+ priv->multicast_filter.enable = __cpu_to_le32(1);
+ priv->multicast_filter.num_addrs = __cpu_to_le32(count);
+ }
+
+ return netdev_hw_addr_list_count(mc_list);
+}
+
+void cw1200_configure_filter(struct ieee80211_hw *dev,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast)
+{
+ struct cw1200_common *priv = dev->priv;
+ bool listening = !!(*total_flags &
+ (FIF_PROMISC_IN_BSS |
+ FIF_OTHER_BSS |
+ FIF_BCN_PRBRESP_PROMISC |
+ FIF_PROBE_REQ));
+
+ *total_flags &= FIF_PROMISC_IN_BSS |
+ FIF_OTHER_BSS |
+ FIF_FCSFAIL |
+ FIF_BCN_PRBRESP_PROMISC |
+ FIF_PROBE_REQ;
+
+ down(&priv->scan.lock);
+ mutex_lock(&priv->conf_mutex);
+
+ priv->rx_filter.promiscuous = (*total_flags & FIF_PROMISC_IN_BSS)
+ ? 1 : 0;
+ priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS |
+ FIF_PROBE_REQ)) ? 1 : 0;
+ priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0;
+ priv->disable_beacon_filter = !(*total_flags &
+ (FIF_BCN_PRBRESP_PROMISC |
+ FIF_PROMISC_IN_BSS |
+ FIF_PROBE_REQ));
+ if (priv->listening != listening) {
+ priv->listening = listening;
+ wsm_lock_tx(priv);
+ cw1200_update_listening(priv, listening);
+ wsm_unlock_tx(priv);
+ }
+ cw1200_update_filtering(priv);
+ mutex_unlock(&priv->conf_mutex);
+ up(&priv->scan.lock);
+}
+
+int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+ u16 queue, const struct ieee80211_tx_queue_params *params)
+{
+ struct cw1200_common *priv = dev->priv;
+ int ret = 0;
+ /* To prevent re-applying PM request OID again and again*/
+ bool old_uapsd_flags;
+
+ mutex_lock(&priv->conf_mutex);
+
+ if (queue < dev->queues) {
+ old_uapsd_flags = priv->uapsd_info.uapsd_flags;
+
+ WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0);
+ ret = wsm_set_tx_queue_params(priv,
+ &priv->tx_queue_params.params[queue], queue);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ WSM_EDCA_SET(&priv->edca, queue, params->aifs,
+ params->cw_min, params->cw_max,
+ params->txop, 0xc8,
+ params->uapsd);
+ ret = wsm_set_edca_params(priv, &priv->edca);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (priv->mode == NL80211_IFTYPE_STATION) {
+ ret = cw1200_set_uapsd_param(priv, &priv->edca);
+ if (!ret && priv->setbssparams_done &&
+ (priv->join_status == CW1200_JOIN_STATUS_STA) &&
+ (old_uapsd_flags != priv->uapsd_info.uapsd_flags))
+ ret = cw1200_set_pm(priv, &priv->powersave_mode);
+ }
+ } else {
+ ret = -EINVAL;
+ }
+
+out:
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+int cw1200_get_stats(struct ieee80211_hw *dev,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct cw1200_common *priv = dev->priv;
+
+ memcpy(stats, &priv->stats, sizeof(*stats));
+ return 0;
+}
+
+int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg)
+{
+ struct wsm_set_pm pm = *arg;
+
+ if (priv->uapsd_info.uapsd_flags != 0)
+ pm.mode &= ~WSM_PSM_FAST_PS_FLAG;
+
+ if (memcmp(&pm, &priv->firmware_ps_mode,
+ sizeof(struct wsm_set_pm))) {
+ priv->firmware_ps_mode = pm;
+ return wsm_set_pm(priv, &pm);
+ } else {
+ return 0;
+ }
+}
+
+int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ int ret = -EOPNOTSUPP;
+ struct cw1200_common *priv = dev->priv;
+ struct ieee80211_key_seq seq;
+
+ mutex_lock(&priv->conf_mutex);
+
+ if (cmd == SET_KEY) {
+ u8 *peer_addr = NULL;
+ int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
+ 1 : 0;
+ int idx = cw1200_alloc_key(priv);
+ struct wsm_add_key *wsm_key = &priv->keys[idx];
+
+ if (idx < 0) {
+ ret = -EINVAL;
+ goto finally;
+ }
+
+ if (sta)
+ peer_addr = sta->addr;
+
+ key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ if (key->keylen > 16) {
+ cw1200_free_key(priv, idx);
+ ret = -EINVAL;
+ goto finally;
+ }
+
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE;
+ memcpy(wsm_key->wep_pairwise.peer,
+ peer_addr, ETH_ALEN);
+ memcpy(wsm_key->wep_pairwise.keydata,
+ &key->key[0], key->keylen);
+ wsm_key->wep_pairwise.keylen = key->keylen;
+ } else {
+ wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT;
+ memcpy(wsm_key->wep_group.keydata,
+ &key->key[0], key->keylen);
+ wsm_key->wep_group.keylen = key->keylen;
+ wsm_key->wep_group.keyid = key->keyidx;
+ }
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ ieee80211_get_key_rx_seq(key, 0, &seq);
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE;
+ memcpy(wsm_key->tkip_pairwise.peer,
+ peer_addr, ETH_ALEN);
+ memcpy(wsm_key->tkip_pairwise.keydata,
+ &key->key[0], 16);
+ memcpy(wsm_key->tkip_pairwise.tx_mic_key,
+ &key->key[16], 8);
+ memcpy(wsm_key->tkip_pairwise.rx_mic_key,
+ &key->key[24], 8);
+ } else {
+ size_t mic_offset =
+ (priv->mode == NL80211_IFTYPE_AP) ?
+ 16 : 24;
+ wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP;
+ memcpy(wsm_key->tkip_group.keydata,
+ &key->key[0], 16);
+ memcpy(wsm_key->tkip_group.rx_mic_key,
+ &key->key[mic_offset], 8);
+
+ wsm_key->tkip_group.rx_seqnum[0] = seq.tkip.iv16 & 0xff;
+ wsm_key->tkip_group.rx_seqnum[1] = (seq.tkip.iv16 >> 8) & 0xff;
+ wsm_key->tkip_group.rx_seqnum[2] = seq.tkip.iv32 & 0xff;
+ wsm_key->tkip_group.rx_seqnum[3] = (seq.tkip.iv32 >> 8) & 0xff;
+ wsm_key->tkip_group.rx_seqnum[4] = (seq.tkip.iv32 >> 16) & 0xff;
+ wsm_key->tkip_group.rx_seqnum[5] = (seq.tkip.iv32 >> 24) & 0xff;
+ wsm_key->tkip_group.rx_seqnum[6] = 0;
+ wsm_key->tkip_group.rx_seqnum[7] = 0;
+
+ wsm_key->tkip_group.keyid = key->keyidx;
+ }
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ ieee80211_get_key_rx_seq(key, 0, &seq);
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE;
+ memcpy(wsm_key->aes_pairwise.peer,
+ peer_addr, ETH_ALEN);
+ memcpy(wsm_key->aes_pairwise.keydata,
+ &key->key[0], 16);
+ } else {
+ wsm_key->type = WSM_KEY_TYPE_AES_GROUP;
+ memcpy(wsm_key->aes_group.keydata,
+ &key->key[0], 16);
+
+ wsm_key->aes_group.rx_seqnum[0] = seq.ccmp.pn[5];
+ wsm_key->aes_group.rx_seqnum[1] = seq.ccmp.pn[4];
+ wsm_key->aes_group.rx_seqnum[2] = seq.ccmp.pn[3];
+ wsm_key->aes_group.rx_seqnum[3] = seq.ccmp.pn[2];
+ wsm_key->aes_group.rx_seqnum[4] = seq.ccmp.pn[1];
+ wsm_key->aes_group.rx_seqnum[5] = seq.ccmp.pn[0];
+ wsm_key->aes_group.rx_seqnum[6] = 0;
+ wsm_key->aes_group.rx_seqnum[7] = 0;
+ wsm_key->aes_group.keyid = key->keyidx;
+ }
+ break;
+ case WLAN_CIPHER_SUITE_SMS4:
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE;
+ memcpy(wsm_key->wapi_pairwise.peer,
+ peer_addr, ETH_ALEN);
+ memcpy(wsm_key->wapi_pairwise.keydata,
+ &key->key[0], 16);
+ memcpy(wsm_key->wapi_pairwise.mic_key,
+ &key->key[16], 16);
+ wsm_key->wapi_pairwise.keyid = key->keyidx;
+ } else {
+ wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP;
+ memcpy(wsm_key->wapi_group.keydata,
+ &key->key[0], 16);
+ memcpy(wsm_key->wapi_group.mic_key,
+ &key->key[16], 16);
+ wsm_key->wapi_group.keyid = key->keyidx;
+ }
+ break;
+ default:
+ pr_warn("Unhandled key type %d\n", key->cipher);
+ cw1200_free_key(priv, idx);
+ ret = -EOPNOTSUPP;
+ goto finally;
+ }
+ ret = wsm_add_key(priv, wsm_key);
+ if (!ret)
+ key->hw_key_idx = idx;
+ else
+ cw1200_free_key(priv, idx);
+ } else if (cmd == DISABLE_KEY) {
+ struct wsm_remove_key wsm_key = {
+ .index = key->hw_key_idx,
+ };
+
+ if (wsm_key.index > WSM_KEY_MAX_INDEX) {
+ ret = -EINVAL;
+ goto finally;
+ }
+
+ cw1200_free_key(priv, wsm_key.index);
+ ret = wsm_remove_key(priv, &wsm_key);
+ } else {
+ pr_warn("Unhandled key command %d\n", cmd);
+ }
+
+finally:
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+void cw1200_wep_key_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, wep_key_work);
+ u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
+ struct cw1200_queue *queue = &priv->tx_queue[queue_id];
+ __le32 wep_default_key_id = __cpu_to_le32(
+ priv->wep_default_key_id);
+
+ pr_debug("[STA] Setting default WEP key: %d\n",
+ priv->wep_default_key_id);
+ wsm_flush_tx(priv);
+ wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID,
+ &wep_default_key_id, sizeof(wep_default_key_id));
+ cw1200_queue_requeue(queue, priv->pending_frame_id);
+ wsm_unlock_tx(priv);
+}
+
+int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+ int ret = 0;
+ __le32 val32;
+ struct cw1200_common *priv = hw->priv;
+
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+ return 0;
+
+ if (value != (u32) -1)
+ val32 = __cpu_to_le32(value);
+ else
+ val32 = 0; /* disabled */
+
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
+ /* device is down, can _not_ set threshold */
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (priv->rts_threshold == value)
+ goto out;
+
+ pr_debug("[STA] Setting RTS threshold: %d\n",
+ priv->rts_threshold);
+
+ /* mutex_lock(&priv->conf_mutex); */
+ ret = wsm_write_mib(priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD,
+ &val32, sizeof(val32));
+ if (!ret)
+ priv->rts_threshold = value;
+ /* mutex_unlock(&priv->conf_mutex); */
+
+out:
+ return ret;
+}
+
+/* If successful, LOCKS the TX queue! */
+static int __cw1200_flush(struct cw1200_common *priv, bool drop)
+{
+ int i, ret;
+
+ for (;;) {
+ /* TODO: correct flush handling is required when dev_stop.
+ * Temporary workaround: 2s
+ */
+ if (drop) {
+ for (i = 0; i < 4; ++i)
+ cw1200_queue_clear(&priv->tx_queue[i]);
+ } else {
+ ret = wait_event_timeout(
+ priv->tx_queue_stats.wait_link_id_empty,
+ cw1200_queue_stats_is_empty(
+ &priv->tx_queue_stats, -1),
+ 2 * HZ);
+ }
+
+ if (!drop && ret <= 0) {
+ ret = -ETIMEDOUT;
+ break;
+ } else {
+ ret = 0;
+ }
+
+ wsm_lock_tx(priv);
+ if (!cw1200_queue_stats_is_empty(&priv->tx_queue_stats, -1)) {
+ /* Highly unlikely: WSM requeued frames. */
+ wsm_unlock_tx(priv);
+ continue;
+ }
+ break;
+ }
+ return ret;
+}
+
+void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+{
+ struct cw1200_common *priv = hw->priv;
+
+ switch (priv->mode) {
+ case NL80211_IFTYPE_MONITOR:
+ drop = true;
+ break;
+ case NL80211_IFTYPE_AP:
+ if (!priv->enable_beacon)
+ drop = true;
+ break;
+ }
+
+ if (!__cw1200_flush(priv, drop))
+ wsm_unlock_tx(priv);
+
+ return;
+}
+
+/* ******************************************************************** */
+/* WSM callbacks */
+
+void cw1200_free_event_queue(struct cw1200_common *priv)
+{
+ LIST_HEAD(list);
+
+ spin_lock(&priv->event_queue_lock);
+ list_splice_init(&priv->event_queue, &list);
+ spin_unlock(&priv->event_queue_lock);
+
+ __cw1200_free_event_queue(&list);
+}
+
+void cw1200_event_handler(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, event_handler);
+ struct cw1200_wsm_event *event;
+ LIST_HEAD(list);
+
+ spin_lock(&priv->event_queue_lock);
+ list_splice_init(&priv->event_queue, &list);
+ spin_unlock(&priv->event_queue_lock);
+
+ list_for_each_entry(event, &list, link) {
+ switch (event->evt.id) {
+ case WSM_EVENT_ERROR:
+ pr_err("Unhandled WSM Error from LMAC\n");
+ break;
+ case WSM_EVENT_BSS_LOST:
+ pr_debug("[CQM] BSS lost.\n");
+ cancel_work_sync(&priv->unjoin_work);
+ if (!down_trylock(&priv->scan.lock)) {
+ cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
+ up(&priv->scan.lock);
+ } else {
+ /* Scan is in progress. Delay reporting.
+ * Scan complete will trigger bss_loss_work
+ */
+ priv->delayed_link_loss = 1;
+ /* Also start a watchdog. */
+ queue_delayed_work(priv->workqueue,
+ &priv->bss_loss_work, 5*HZ);
+ }
+ break;
+ case WSM_EVENT_BSS_REGAINED:
+ pr_debug("[CQM] BSS regained.\n");
+ cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+ cancel_work_sync(&priv->unjoin_work);
+ break;
+ case WSM_EVENT_RADAR_DETECTED:
+ wiphy_info(priv->hw->wiphy, "radar pulse detected\n");
+ break;
+ case WSM_EVENT_RCPI_RSSI:
+ {
+ /* RSSI: signed Q8.0, RCPI: unsigned Q7.1
+ * RSSI = RCPI / 2 - 110
+ */
+ int rcpi_rssi = (int)(event->evt.data & 0xFF);
+ int cqm_evt;
+ if (priv->cqm_use_rssi)
+ rcpi_rssi = (s8)rcpi_rssi;
+ else
+ rcpi_rssi = rcpi_rssi / 2 - 110;
+
+ cqm_evt = (rcpi_rssi <= priv->cqm_rssi_thold) ?
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+ pr_debug("[CQM] RSSI event: %d.\n", rcpi_rssi);
+ ieee80211_cqm_rssi_notify(priv->vif, cqm_evt,
+ GFP_KERNEL);
+ break;
+ }
+ case WSM_EVENT_BT_INACTIVE:
+ pr_warn("Unhandled BT INACTIVE from LMAC\n");
+ break;
+ case WSM_EVENT_BT_ACTIVE:
+ pr_warn("Unhandled BT ACTIVE from LMAC\n");
+ break;
+ }
+ }
+ __cw1200_free_event_queue(&list);
+}
+
+void cw1200_bss_loss_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, bss_loss_work.work);
+
+ pr_debug("[CQM] Reporting connection loss.\n");
+ wsm_lock_tx(priv);
+ if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+}
+
+void cw1200_bss_params_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, bss_params_work);
+ mutex_lock(&priv->conf_mutex);
+
+ priv->bss_params.reset_beacon_loss = 1;
+ wsm_set_bss_params(priv, &priv->bss_params);
+ priv->bss_params.reset_beacon_loss = 0;
+
+ mutex_unlock(&priv->conf_mutex);
+}
+
+/* ******************************************************************** */
+/* Internal API */
+
+/* This function is called to Parse the SDD file
+ * to extract listen_interval and PTA related information
+ * sdd is a TLV: u8 id, u8 len, u8 data[]
+ */
+static int cw1200_parse_sdd_file(struct cw1200_common *priv)
+{
+ const u8 *p = priv->sdd->data;
+ int ret = 0;
+
+ while (p + 2 <= priv->sdd->data + priv->sdd->size) {
+ if (p + p[1] + 2 > priv->sdd->data + priv->sdd->size) {
+ pr_warn("Malformed sdd structure\n");
+ return -1;
+ }
+ switch (p[0]) {
+ case SDD_PTA_CFG_ELT_ID: {
+ u16 v;
+ if (p[1] < 4) {
+ pr_warn("SDD_PTA_CFG_ELT_ID malformed\n");
+ ret = -1;
+ break;
+ }
+ v = le16_to_cpu(*((u16 *)(p + 2)));
+ if (!v) /* non-zero means this is enabled */
+ break;
+
+ v = le16_to_cpu(*((u16 *)(p + 4)));
+ priv->conf_listen_interval = (v >> 7) & 0x1F;
+ pr_debug("PTA found; Listen Interval %d\n",
+ priv->conf_listen_interval);
+ break;
+ }
+ case SDD_REFERENCE_FREQUENCY_ELT_ID: {
+ u16 clk = le16_to_cpu(*((u16 *)(p + 2)));
+ if (clk != priv->hw_refclk)
+ pr_warn("SDD file doesn't match configured refclk (%d vs %d)\n",
+ clk, priv->hw_refclk);
+ break;
+ }
+ default:
+ break;
+ }
+ p += p[1] + 2;
+ }
+
+ if (!priv->bt_present) {
+ pr_debug("PTA element NOT found.\n");
+ priv->conf_listen_interval = 0;
+ }
+ return ret;
+}
+
+int cw1200_setup_mac(struct cw1200_common *priv)
+{
+ int ret = 0;
+
+ /* NOTE: There is a bug in FW: it reports signal
+ * as RSSI if RSSI subscription is enabled.
+ * It's not enough to set WSM_RCPI_RSSI_USE_RSSI.
+ *
+ * NOTE2: RSSI based reports have been switched to RCPI, since
+ * FW has a bug and RSSI reported values are not stable,
+ * what can leads to signal level oscilations in user-end applications
+ */
+ struct wsm_rcpi_rssi_threshold threshold = {
+ .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE |
+ WSM_RCPI_RSSI_DONT_USE_UPPER |
+ WSM_RCPI_RSSI_DONT_USE_LOWER,
+ .rollingAverageCount = 16,
+ };
+
+ struct wsm_configuration cfg = {
+ .dot11StationId = &priv->mac_addr[0],
+ };
+
+ /* Remember the decission here to make sure, we will handle
+ * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS
+ */
+ if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI)
+ priv->cqm_use_rssi = true;
+
+ if (!priv->sdd) {
+ ret = request_firmware(&priv->sdd, priv->sdd_path, priv->pdev);
+ if (ret) {
+ pr_err("Can't load sdd file %s.\n", priv->sdd_path);
+ return ret;
+ }
+ cw1200_parse_sdd_file(priv);
+ }
+
+ cfg.dpdData = priv->sdd->data;
+ cfg.dpdData_size = priv->sdd->size;
+ ret = wsm_configuration(priv, &cfg);
+ if (ret)
+ return ret;
+
+ /* Configure RSSI/SCPI reporting as RSSI. */
+ wsm_set_rcpi_rssi_threshold(priv, &threshold);
+
+ return 0;
+}
+
+static void cw1200_join_complete(struct cw1200_common *priv)
+{
+ pr_debug("[STA] Join complete (%d)\n", priv->join_complete_status);
+
+ priv->join_pending = false;
+ if (priv->join_complete_status) {
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ cw1200_update_listening(priv, priv->listening);
+ cw1200_do_unjoin(priv);
+ ieee80211_connection_loss(priv->vif);
+ } else {
+ if (priv->mode == NL80211_IFTYPE_ADHOC)
+ priv->join_status = CW1200_JOIN_STATUS_IBSS;
+ else
+ priv->join_status = CW1200_JOIN_STATUS_PRE_STA;
+ }
+ wsm_unlock_tx(priv); /* Clearing the lock held before do_join() */
+}
+
+void cw1200_join_complete_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, join_complete_work);
+ mutex_lock(&priv->conf_mutex);
+ cw1200_join_complete(priv);
+ mutex_unlock(&priv->conf_mutex);
+}
+
+void cw1200_join_complete_cb(struct cw1200_common *priv,
+ struct wsm_join_complete *arg)
+{
+ pr_debug("[STA] cw1200_join_complete_cb called, status=%d.\n",
+ arg->status);
+
+ if (cancel_delayed_work(&priv->join_timeout)) {
+ priv->join_complete_status = arg->status;
+ queue_work(priv->workqueue, &priv->join_complete_work);
+ }
+}
+
+/* MUST be called with tx_lock held! It will be unlocked for us. */
+static void cw1200_do_join(struct cw1200_common *priv)
+{
+ const u8 *bssid;
+ struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+ struct cfg80211_bss *bss = NULL;
+ struct wsm_protected_mgmt_policy mgmt_policy;
+ struct wsm_join join = {
+ .mode = conf->ibss_joined ?
+ WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS,
+ .preamble_type = WSM_JOIN_PREAMBLE_LONG,
+ .probe_for_join = 1,
+ .atim_window = 0,
+ .basic_rate_set = cw1200_rate_mask_to_wsm(priv,
+ conf->basic_rates),
+ };
+ if (delayed_work_pending(&priv->join_timeout)) {
+ pr_warn("[STA] - Join request already pending, skipping..\n");
+ wsm_unlock_tx(priv);
+ return;
+ }
+
+ if (priv->join_status)
+ cw1200_do_unjoin(priv);
+
+ bssid = priv->vif->bss_conf.bssid;
+
+ bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel,
+ bssid, NULL, 0, 0, 0);
+
+ if (!bss && !conf->ibss_joined) {
+ wsm_unlock_tx(priv);
+ return;
+ }
+
+ mutex_lock(&priv->conf_mutex);
+
+ /* Under the conf lock: check scan status and
+ * bail out if it is in progress.
+ */
+ if (atomic_read(&priv->scan.in_progress)) {
+ wsm_unlock_tx(priv);
+ goto done_put;
+ }
+
+ priv->join_pending = true;
+
+ /* Sanity check basic rates */
+ if (!join.basic_rate_set)
+ join.basic_rate_set = 7;
+
+ /* Sanity check beacon interval */
+ if (!priv->beacon_int)
+ priv->beacon_int = 1;
+
+ join.beacon_interval = priv->beacon_int;
+
+ /* BT Coex related changes */
+ if (priv->bt_present) {
+ if (((priv->conf_listen_interval * 100) %
+ priv->beacon_int) == 0)
+ priv->listen_interval =
+ ((priv->conf_listen_interval * 100) /
+ priv->beacon_int);
+ else
+ priv->listen_interval =
+ ((priv->conf_listen_interval * 100) /
+ priv->beacon_int + 1);
+ }
+
+ if (priv->hw->conf.ps_dtim_period)
+ priv->join_dtim_period = priv->hw->conf.ps_dtim_period;
+ join.dtim_period = priv->join_dtim_period;
+
+ join.channel_number = priv->channel->hw_value;
+ join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
+ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+
+ memcpy(join.bssid, bssid, sizeof(join.bssid));
+
+ pr_debug("[STA] Join BSSID: %pM DTIM: %d, interval: %d\n",
+ join.bssid,
+ join.dtim_period, priv->beacon_int);
+
+ if (!conf->ibss_joined) {
+ const u8 *ssidie;
+ rcu_read_lock();
+ ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
+ if (ssidie) {
+ join.ssid_len = ssidie[1];
+ memcpy(join.ssid, &ssidie[2], join.ssid_len);
+ }
+ rcu_read_unlock();
+ }
+
+ if (priv->vif->p2p) {
+ join.flags |= WSM_JOIN_FLAGS_P2P_GO;
+ join.basic_rate_set =
+ cw1200_rate_mask_to_wsm(priv, 0xFF0);
+ }
+
+ /* Enable asynchronous join calls */
+ if (!conf->ibss_joined) {
+ join.flags |= WSM_JOIN_FLAGS_FORCE;
+ join.flags |= WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND;
+ }
+
+ wsm_flush_tx(priv);
+
+ /* Stay Awake for Join and Auth Timeouts and a bit more */
+ cw1200_pm_stay_awake(&priv->pm_state,
+ CW1200_JOIN_TIMEOUT + CW1200_AUTH_TIMEOUT);
+
+ cw1200_update_listening(priv, false);
+
+ /* Turn on Block ACKs */
+ wsm_set_block_ack_policy(priv, priv->ba_tx_tid_mask,
+ priv->ba_rx_tid_mask);
+
+ /* Set up timeout */
+ if (join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND) {
+ priv->join_status = CW1200_JOIN_STATUS_JOINING;
+ queue_delayed_work(priv->workqueue,
+ &priv->join_timeout,
+ CW1200_JOIN_TIMEOUT);
+ }
+
+ /* 802.11w protected mgmt frames */
+ mgmt_policy.protectedMgmtEnable = 0;
+ mgmt_policy.unprotectedMgmtFramesAllowed = 1;
+ mgmt_policy.encryptionForAuthFrame = 1;
+ wsm_set_protected_mgmt_policy(priv, &mgmt_policy);
+
+ /* Perform actual join */
+ if (wsm_join(priv, &join)) {
+ pr_err("[STA] cw1200_join_work: wsm_join failed!\n");
+ cancel_delayed_work_sync(&priv->join_timeout);
+ cw1200_update_listening(priv, priv->listening);
+ /* Tx lock still held, unjoin will clear it. */
+ if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+ } else {
+ if (!(join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND))
+ cw1200_join_complete(priv); /* Will clear tx_lock */
+
+ /* Upload keys */
+ cw1200_upload_keys(priv);
+
+ /* Due to beacon filtering it is possible that the
+ * AP's beacon is not known for the mac80211 stack.
+ * Disable filtering temporary to make sure the stack
+ * receives at least one
+ */
+ priv->disable_beacon_filter = true;
+ }
+ cw1200_update_filtering(priv);
+
+done_put:
+ mutex_unlock(&priv->conf_mutex);
+ if (bss)
+ cfg80211_put_bss(priv->hw->wiphy, bss);
+}
+
+void cw1200_join_timeout(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, join_timeout.work);
+ pr_debug("[WSM] Join timed out.\n");
+ wsm_lock_tx(priv);
+ if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+}
+
+static void cw1200_do_unjoin(struct cw1200_common *priv)
+{
+ struct wsm_reset reset = {
+ .reset_statistics = true,
+ };
+
+ cancel_delayed_work_sync(&priv->join_timeout);
+
+ mutex_lock(&priv->conf_mutex);
+ priv->join_pending = false;
+
+ if (atomic_read(&priv->scan.in_progress)) {
+ if (priv->delayed_unjoin)
+ wiphy_dbg(priv->hw->wiphy, "Delayed unjoin is already scheduled.\n");
+ else
+ priv->delayed_unjoin = true;
+ goto done;
+ }
+
+ priv->delayed_link_loss = false;
+
+ if (!priv->join_status)
+ goto done;
+
+ if (priv->join_status > CW1200_JOIN_STATUS_IBSS) {
+ wiphy_err(priv->hw->wiphy, "Unexpected: join status: %d\n",
+ priv->join_status);
+ BUG_ON(1);
+ }
+
+ cancel_work_sync(&priv->update_filtering_work);
+ cancel_work_sync(&priv->set_beacon_wakeup_period_work);
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+
+ /* Unjoin is a reset. */
+ wsm_flush_tx(priv);
+ wsm_keep_alive_period(priv, 0);
+ wsm_reset(priv, &reset);
+ wsm_set_output_power(priv, priv->output_power * 10);
+ priv->join_dtim_period = 0;
+ cw1200_setup_mac(priv);
+ cw1200_free_event_queue(priv);
+ cancel_work_sync(&priv->event_handler);
+ cw1200_update_listening(priv, priv->listening);
+ cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+
+ /* Disable Block ACKs */
+ wsm_set_block_ack_policy(priv, 0, 0);
+
+ priv->disable_beacon_filter = false;
+ cw1200_update_filtering(priv);
+ memset(&priv->association_mode, 0,
+ sizeof(priv->association_mode));
+ memset(&priv->bss_params, 0, sizeof(priv->bss_params));
+ priv->setbssparams_done = false;
+ memset(&priv->firmware_ps_mode, 0,
+ sizeof(priv->firmware_ps_mode));
+
+ pr_debug("[STA] Unjoin completed.\n");
+
+done:
+ mutex_unlock(&priv->conf_mutex);
+}
+
+void cw1200_unjoin_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, unjoin_work);
+
+ cw1200_do_unjoin(priv);
+
+ /* Tell the stack we're dead */
+ ieee80211_connection_loss(priv->vif);
+
+ wsm_unlock_tx(priv);
+}
+
+int cw1200_enable_listening(struct cw1200_common *priv)
+{
+ struct wsm_start start = {
+ .mode = WSM_START_MODE_P2P_DEV,
+ .band = WSM_PHY_BAND_2_4G,
+ .beacon_interval = 100,
+ .dtim_period = 1,
+ .probe_delay = 0,
+ .basic_rate_set = 0x0F,
+ };
+
+ if (priv->channel) {
+ start.band = priv->channel->band == IEEE80211_BAND_5GHZ ?
+ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+ start.channel_number = priv->channel->hw_value;
+ } else {
+ start.band = WSM_PHY_BAND_2_4G;
+ start.channel_number = 1;
+ }
+
+ return wsm_start(priv, &start);
+}
+
+int cw1200_disable_listening(struct cw1200_common *priv)
+{
+ int ret;
+ struct wsm_reset reset = {
+ .reset_statistics = true,
+ };
+ ret = wsm_reset(priv, &reset);
+ return ret;
+}
+
+void cw1200_update_listening(struct cw1200_common *priv, bool enabled)
+{
+ if (enabled) {
+ if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) {
+ if (!cw1200_enable_listening(priv))
+ priv->join_status = CW1200_JOIN_STATUS_MONITOR;
+ wsm_set_probe_responder(priv, true);
+ }
+ } else {
+ if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
+ if (!cw1200_disable_listening(priv))
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ wsm_set_probe_responder(priv, false);
+ }
+ }
+}
+
+int cw1200_set_uapsd_param(struct cw1200_common *priv,
+ const struct wsm_edca_params *arg)
+{
+ int ret;
+ u16 uapsd_flags = 0;
+
+ /* Here's the mapping AC [queue, bit]
+ * VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0]
+ */
+
+ if (arg->uapsd_enable[0])
+ uapsd_flags |= 1 << 3;
+
+ if (arg->uapsd_enable[1])
+ uapsd_flags |= 1 << 2;
+
+ if (arg->uapsd_enable[2])
+ uapsd_flags |= 1 << 1;
+
+ if (arg->uapsd_enable[3])
+ uapsd_flags |= 1;
+
+ /* Currently pseudo U-APSD operation is not supported, so setting
+ * MinAutoTriggerInterval, MaxAutoTriggerInterval and
+ * AutoTriggerStep to 0
+ */
+
+ priv->uapsd_info.uapsd_flags = cpu_to_le16(uapsd_flags);
+ priv->uapsd_info.min_auto_trigger_interval = 0;
+ priv->uapsd_info.max_auto_trigger_interval = 0;
+ priv->uapsd_info.auto_trigger_step = 0;
+
+ ret = wsm_set_uapsd_info(priv, &priv->uapsd_info);
+ return ret;
+}
+
+/* ******************************************************************** */
+/* AP API */
+
+int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct cw1200_common *priv = hw->priv;
+ struct cw1200_sta_priv *sta_priv =
+ (struct cw1200_sta_priv *)&sta->drv_priv;
+ struct cw1200_link_entry *entry;
+ struct sk_buff *skb;
+
+ if (priv->mode != NL80211_IFTYPE_AP)
+ return 0;
+
+ sta_priv->link_id = cw1200_find_link_id(priv, sta->addr);
+ if (WARN_ON(!sta_priv->link_id)) {
+ wiphy_info(priv->hw->wiphy,
+ "[AP] No more link IDs available.\n");
+ return -ENOENT;
+ }
+
+ entry = &priv->link_id_db[sta_priv->link_id - 1];
+ spin_lock_bh(&priv->ps_state_lock);
+ if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) ==
+ IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
+ priv->sta_asleep_mask |= BIT(sta_priv->link_id);
+ entry->status = CW1200_LINK_HARD;
+ while ((skb = skb_dequeue(&entry->rx_queue)))
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ spin_unlock_bh(&priv->ps_state_lock);
+ return 0;
+}
+
+int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct cw1200_common *priv = hw->priv;
+ struct cw1200_sta_priv *sta_priv =
+ (struct cw1200_sta_priv *)&sta->drv_priv;
+ struct cw1200_link_entry *entry;
+
+ if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id)
+ return 0;
+
+ entry = &priv->link_id_db[sta_priv->link_id - 1];
+ spin_lock_bh(&priv->ps_state_lock);
+ entry->status = CW1200_LINK_RESERVE;
+ entry->timestamp = jiffies;
+ wsm_lock_tx_async(priv);
+ if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
+ wsm_unlock_tx(priv);
+ spin_unlock_bh(&priv->ps_state_lock);
+ flush_workqueue(priv->workqueue);
+ return 0;
+}
+
+static void __cw1200_sta_notify(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd notify_cmd,
+ int link_id)
+{
+ struct cw1200_common *priv = dev->priv;
+ u32 bit, prev;
+
+ /* Zero link id means "for all link IDs" */
+ if (link_id)
+ bit = BIT(link_id);
+ else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE))
+ bit = 0;
+ else
+ bit = priv->link_id_map;
+ prev = priv->sta_asleep_mask & bit;
+
+ switch (notify_cmd) {
+ case STA_NOTIFY_SLEEP:
+ if (!prev) {
+ if (priv->buffered_multicasts &&
+ !priv->sta_asleep_mask)
+ queue_work(priv->workqueue,
+ &priv->multicast_start_work);
+ priv->sta_asleep_mask |= bit;
+ }
+ break;
+ case STA_NOTIFY_AWAKE:
+ if (prev) {
+ priv->sta_asleep_mask &= ~bit;
+ priv->pspoll_mask &= ~bit;
+ if (priv->tx_multicast && link_id &&
+ !priv->sta_asleep_mask)
+ queue_work(priv->workqueue,
+ &priv->multicast_stop_work);
+ cw1200_bh_wakeup(priv);
+ }
+ break;
+ }
+}
+
+void cw1200_sta_notify(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd notify_cmd,
+ struct ieee80211_sta *sta)
+{
+ struct cw1200_common *priv = dev->priv;
+ struct cw1200_sta_priv *sta_priv =
+ (struct cw1200_sta_priv *)&sta->drv_priv;
+
+ spin_lock_bh(&priv->ps_state_lock);
+ __cw1200_sta_notify(dev, vif, notify_cmd, sta_priv->link_id);
+ spin_unlock_bh(&priv->ps_state_lock);
+}
+
+static void cw1200_ps_notify(struct cw1200_common *priv,
+ int link_id, bool ps)
+{
+ if (link_id > CW1200_MAX_STA_IN_AP_MODE)
+ return;
+
+ pr_debug("%s for LinkId: %d. STAs asleep: %.8X\n",
+ ps ? "Stop" : "Start",
+ link_id, priv->sta_asleep_mask);
+
+ __cw1200_sta_notify(priv->hw, priv->vif,
+ ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id);
+}
+
+static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set)
+{
+ struct sk_buff *skb;
+ struct wsm_update_ie update_ie = {
+ .what = WSM_UPDATE_IE_BEACON,
+ .count = 1,
+ };
+ u16 tim_offset, tim_length;
+
+ pr_debug("[AP] mcast: %s.\n", aid0_bit_set ? "ena" : "dis");
+
+ skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
+ &tim_offset, &tim_length);
+ if (!skb) {
+ if (!__cw1200_flush(priv, true))
+ wsm_unlock_tx(priv);
+ return -ENOENT;
+ }
+
+ if (tim_offset && tim_length >= 6) {
+ /* Ignore DTIM count from mac80211:
+ * firmware handles DTIM internally.
+ */
+ skb->data[tim_offset + 2] = 0;
+
+ /* Set/reset aid0 bit */
+ if (aid0_bit_set)
+ skb->data[tim_offset + 4] |= 1;
+ else
+ skb->data[tim_offset + 4] &= ~1;
+ }
+
+ update_ie.ies = &skb->data[tim_offset];
+ update_ie.length = tim_length;
+ wsm_update_ie(priv, &update_ie);
+
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+void cw1200_set_tim_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, set_tim_work);
+ (void)cw1200_set_tim_impl(priv, priv->aid0_bit_set);
+}
+
+int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
+ bool set)
+{
+ struct cw1200_common *priv = dev->priv;
+ queue_work(priv->workqueue, &priv->set_tim_work);
+ return 0;
+}
+
+void cw1200_set_cts_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, set_cts_work);
+
+ u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0};
+ struct wsm_update_ie update_ie = {
+ .what = WSM_UPDATE_IE_BEACON,
+ .count = 1,
+ .ies = erp_ie,
+ .length = 3,
+ };
+ u32 erp_info;
+ __le32 use_cts_prot;
+ mutex_lock(&priv->conf_mutex);
+ erp_info = priv->erp_info;
+ mutex_unlock(&priv->conf_mutex);
+ use_cts_prot =
+ erp_info & WLAN_ERP_USE_PROTECTION ?
+ __cpu_to_le32(1) : 0;
+
+ erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info;
+
+ pr_debug("[STA] ERP information 0x%x\n", erp_info);
+
+ wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION,
+ &use_cts_prot, sizeof(use_cts_prot));
+ wsm_update_ie(priv, &update_ie);
+
+ return;
+}
+
+static int cw1200_set_btcoexinfo(struct cw1200_common *priv)
+{
+ struct wsm_override_internal_txrate arg;
+ int ret = 0;
+
+ if (priv->mode == NL80211_IFTYPE_STATION) {
+ /* Plumb PSPOLL and NULL template */
+ cw1200_upload_pspoll(priv);
+ cw1200_upload_null(priv);
+ cw1200_upload_qosnull(priv);
+ } else {
+ return 0;
+ }
+
+ memset(&arg, 0, sizeof(struct wsm_override_internal_txrate));
+
+ if (!priv->vif->p2p) {
+ /* STATION mode */
+ if (priv->bss_params.operational_rate_set & ~0xF) {
+ pr_debug("[STA] STA has ERP rates\n");
+ /* G or BG mode */
+ arg.internalTxRate = (__ffs(
+ priv->bss_params.operational_rate_set & ~0xF));
+ } else {
+ pr_debug("[STA] STA has non ERP rates\n");
+ /* B only mode */
+ arg.internalTxRate = (__ffs(priv->association_mode.basic_rate_set));
+ }
+ arg.nonErpInternalTxRate = (__ffs(priv->association_mode.basic_rate_set));
+ } else {
+ /* P2P mode */
+ arg.internalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF));
+ arg.nonErpInternalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF));
+ }
+
+ pr_debug("[STA] BTCOEX_INFO MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n",
+ priv->mode,
+ arg.internalTxRate,
+ arg.nonErpInternalTxRate);
+
+ ret = wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
+ &arg, sizeof(arg));
+
+ return ret;
+}
+
+void cw1200_bss_info_changed(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed)
+{
+ struct cw1200_common *priv = dev->priv;
+ bool do_join = false;
+
+ mutex_lock(&priv->conf_mutex);
+
+ pr_debug("BSS CHANGED: %08x\n", changed);
+
+ /* TODO: BSS_CHANGED_QOS */
+ /* TODO: BSS_CHANGED_TXPOWER */
+
+ if (changed & BSS_CHANGED_ARP_FILTER) {
+ struct wsm_mib_arp_ipv4_filter filter = {0};
+ int i;
+
+ pr_debug("[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n",
+ info->arp_addr_cnt);
+
+ /* Currently only one IP address is supported by firmware.
+ * In case of more IPs arp filtering will be disabled.
+ */
+ if (info->arp_addr_cnt > 0 &&
+ info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) {
+ for (i = 0; i < info->arp_addr_cnt; i++) {
+ filter.ipv4addrs[i] = info->arp_addr_list[i];
+ pr_debug("[STA] addr[%d]: 0x%X\n",
+ i, filter.ipv4addrs[i]);
+ }
+ filter.enable = __cpu_to_le32(1);
+ }
+
+ pr_debug("[STA] arp ip filter enable: %d\n",
+ __le32_to_cpu(filter.enable));
+
+ wsm_set_arp_ipv4_filter(priv, &filter);
+ }
+
+ if (changed &
+ (BSS_CHANGED_BEACON |
+ BSS_CHANGED_AP_PROBE_RESP |
+ BSS_CHANGED_BSSID |
+ BSS_CHANGED_SSID |
+ BSS_CHANGED_IBSS)) {
+ pr_debug("BSS_CHANGED_BEACON\n");
+ priv->beacon_int = info->beacon_int;
+ cw1200_update_beaconing(priv);
+ cw1200_upload_beacon(priv);
+ }
+
+ if (changed & BSS_CHANGED_BEACON_ENABLED) {
+ pr_debug("BSS_CHANGED_BEACON_ENABLED (%d)\n", info->enable_beacon);
+
+ if (priv->enable_beacon != info->enable_beacon) {
+ cw1200_enable_beaconing(priv, info->enable_beacon);
+ priv->enable_beacon = info->enable_beacon;
+ }
+ }
+
+ if (changed & BSS_CHANGED_BEACON_INT) {
+ pr_debug("CHANGED_BEACON_INT\n");
+ if (info->ibss_joined)
+ do_join = true;
+ else if (priv->join_status == CW1200_JOIN_STATUS_AP)
+ cw1200_update_beaconing(priv);
+ }
+
+ /* assoc/disassoc, or maybe AID changed */
+ if (changed & BSS_CHANGED_ASSOC) {
+ wsm_lock_tx(priv);
+ priv->wep_default_key_id = -1;
+ wsm_unlock_tx(priv);
+ }
+
+ if (changed & BSS_CHANGED_BSSID) {
+ pr_debug("BSS_CHANGED_BSSID\n");
+ do_join = true;
+ }
+
+ if (changed &
+ (BSS_CHANGED_ASSOC |
+ BSS_CHANGED_BSSID |
+ BSS_CHANGED_IBSS |
+ BSS_CHANGED_BASIC_RATES |
+ BSS_CHANGED_HT)) {
+ pr_debug("BSS_CHANGED_ASSOC\n");
+ if (info->assoc) {
+ if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) {
+ ieee80211_connection_loss(vif);
+ mutex_unlock(&priv->conf_mutex);
+ return;
+ } else if (priv->join_status == CW1200_JOIN_STATUS_PRE_STA) {
+ priv->join_status = CW1200_JOIN_STATUS_STA;
+ }
+ } else {
+ do_join = true;
+ }
+
+ if (info->assoc || info->ibss_joined) {
+ struct ieee80211_sta *sta = NULL;
+ u32 val = 0;
+
+ if (info->dtim_period)
+ priv->join_dtim_period = info->dtim_period;
+ priv->beacon_int = info->beacon_int;
+
+ rcu_read_lock();
+
+ if (info->bssid && !info->ibss_joined)
+ sta = ieee80211_find_sta(vif, info->bssid);
+ if (sta) {
+ priv->ht_info.ht_cap = sta->ht_cap;
+ priv->bss_params.operational_rate_set =
+ cw1200_rate_mask_to_wsm(priv,
+ sta->supp_rates[priv->channel->band]);
+ priv->ht_info.channel_type = cfg80211_get_chandef_type(&dev->conf.chandef);
+ priv->ht_info.operation_mode = info->ht_operation_mode;
+ } else {
+ memset(&priv->ht_info, 0,
+ sizeof(priv->ht_info));
+ priv->bss_params.operational_rate_set = -1;
+ }
+ rcu_read_unlock();
+
+ /* Non Greenfield stations present */
+ if (priv->ht_info.operation_mode &
+ IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT)
+ val |= WSM_NON_GREENFIELD_STA_PRESENT;
+
+ /* Set HT protection method */
+ val |= (priv->ht_info.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION) << 2;
+
+ /* TODO:
+ * STBC_param.dual_cts
+ * STBC_param.LSIG_TXOP_FILL
+ */
+
+ val = cpu_to_le32(val);
+ wsm_write_mib(priv, WSM_MIB_ID_SET_HT_PROTECTION,
+ &val, sizeof(val));
+
+ priv->association_mode.greenfield =
+ cw1200_ht_greenfield(&priv->ht_info);
+ priv->association_mode.flags =
+ WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES |
+ WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE |
+ WSM_ASSOCIATION_MODE_USE_HT_MODE |
+ WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET |
+ WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING;
+ priv->association_mode.preamble =
+ info->use_short_preamble ?
+ WSM_JOIN_PREAMBLE_SHORT :
+ WSM_JOIN_PREAMBLE_LONG;
+ priv->association_mode.basic_rate_set = __cpu_to_le32(
+ cw1200_rate_mask_to_wsm(priv,
+ info->basic_rates));
+ priv->association_mode.mpdu_start_spacing =
+ cw1200_ht_ampdu_density(&priv->ht_info);
+
+ cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+ cancel_work_sync(&priv->unjoin_work);
+
+ priv->bss_params.beacon_lost_count = priv->cqm_beacon_loss_count;
+ priv->bss_params.aid = info->aid;
+
+ if (priv->join_dtim_period < 1)
+ priv->join_dtim_period = 1;
+
+ pr_debug("[STA] DTIM %d, interval: %d\n",
+ priv->join_dtim_period, priv->beacon_int);
+ pr_debug("[STA] Preamble: %d, Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n",
+ priv->association_mode.preamble,
+ priv->association_mode.greenfield,
+ priv->bss_params.aid,
+ priv->bss_params.operational_rate_set,
+ priv->association_mode.basic_rate_set);
+ wsm_set_association_mode(priv, &priv->association_mode);
+
+ if (!info->ibss_joined) {
+ wsm_keep_alive_period(priv, 30 /* sec */);
+ wsm_set_bss_params(priv, &priv->bss_params);
+ priv->setbssparams_done = true;
+ cw1200_set_beacon_wakeup_period_work(&priv->set_beacon_wakeup_period_work);
+ cw1200_set_pm(priv, &priv->powersave_mode);
+ }
+ if (priv->vif->p2p) {
+ pr_debug("[STA] Setting p2p powersave configuration.\n");
+ wsm_set_p2p_ps_modeinfo(priv,
+ &priv->p2p_ps_modeinfo);
+ }
+ if (priv->bt_present)
+ cw1200_set_btcoexinfo(priv);
+ } else {
+ memset(&priv->association_mode, 0,
+ sizeof(priv->association_mode));
+ memset(&priv->bss_params, 0, sizeof(priv->bss_params));
+ }
+ }
+
+ /* ERP Protection */
+ if (changed & (BSS_CHANGED_ASSOC |
+ BSS_CHANGED_ERP_CTS_PROT |
+ BSS_CHANGED_ERP_PREAMBLE)) {
+ u32 prev_erp_info = priv->erp_info;
+ if (info->use_cts_prot)
+ priv->erp_info |= WLAN_ERP_USE_PROTECTION;
+ else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT))
+ priv->erp_info &= ~WLAN_ERP_USE_PROTECTION;
+
+ if (info->use_short_preamble)
+ priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE;
+ else
+ priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE;
+
+ pr_debug("[STA] ERP Protection: %x\n", priv->erp_info);
+
+ if (prev_erp_info != priv->erp_info)
+ queue_work(priv->workqueue, &priv->set_cts_work);
+ }
+
+ /* ERP Slottime */
+ if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) {
+ __le32 slot_time = info->use_short_slot ?
+ __cpu_to_le32(9) : __cpu_to_le32(20);
+ pr_debug("[STA] Slot time: %d us.\n",
+ __le32_to_cpu(slot_time));
+ wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME,
+ &slot_time, sizeof(slot_time));
+ }
+
+ if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) {
+ struct wsm_rcpi_rssi_threshold threshold = {
+ .rollingAverageCount = 8,
+ };
+ pr_debug("[CQM] RSSI threshold subscribe: %d +- %d\n",
+ info->cqm_rssi_thold, info->cqm_rssi_hyst);
+ priv->cqm_rssi_thold = info->cqm_rssi_thold;
+ priv->cqm_rssi_hyst = info->cqm_rssi_hyst;
+
+ if (info->cqm_rssi_thold || info->cqm_rssi_hyst) {
+ /* RSSI subscription enabled */
+ /* TODO: It's not a correct way of setting threshold.
+ * Upper and lower must be set equal here and adjusted
+ * in callback. However current implementation is much
+ * more relaible and stable.
+ */
+
+ /* RSSI: signed Q8.0, RCPI: unsigned Q7.1
+ * RSSI = RCPI / 2 - 110
+ */
+ if (priv->cqm_use_rssi) {
+ threshold.upperThreshold =
+ info->cqm_rssi_thold + info->cqm_rssi_hyst;
+ threshold.lowerThreshold =
+ info->cqm_rssi_thold;
+ threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI;
+ } else {
+ threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110) * 2;
+ threshold.lowerThreshold = (info->cqm_rssi_thold + 110) * 2;
+ }
+ threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE;
+ } else {
+ /* There is a bug in FW, see sta.c. We have to enable
+ * dummy subscription to get correct RSSI values.
+ */
+ threshold.rssiRcpiMode |=
+ WSM_RCPI_RSSI_THRESHOLD_ENABLE |
+ WSM_RCPI_RSSI_DONT_USE_UPPER |
+ WSM_RCPI_RSSI_DONT_USE_LOWER;
+ if (priv->cqm_use_rssi)
+ threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI;
+ }
+ wsm_set_rcpi_rssi_threshold(priv, &threshold);
+ }
+ mutex_unlock(&priv->conf_mutex);
+
+ if (do_join) {
+ wsm_lock_tx(priv);
+ cw1200_do_join(priv); /* Will unlock it for us */
+ }
+}
+
+void cw1200_multicast_start_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, multicast_start_work);
+ long tmo = priv->join_dtim_period *
+ (priv->beacon_int + 20) * HZ / 1024;
+
+ cancel_work_sync(&priv->multicast_stop_work);
+
+ if (!priv->aid0_bit_set) {
+ wsm_lock_tx(priv);
+ cw1200_set_tim_impl(priv, true);
+ priv->aid0_bit_set = true;
+ mod_timer(&priv->mcast_timeout, jiffies + tmo);
+ wsm_unlock_tx(priv);
+ }
+}
+
+void cw1200_multicast_stop_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, multicast_stop_work);
+
+ if (priv->aid0_bit_set) {
+ del_timer_sync(&priv->mcast_timeout);
+ wsm_lock_tx(priv);
+ priv->aid0_bit_set = false;
+ cw1200_set_tim_impl(priv, false);
+ wsm_unlock_tx(priv);
+ }
+}
+
+void cw1200_mcast_timeout(unsigned long arg)
+{
+ struct cw1200_common *priv =
+ (struct cw1200_common *)arg;
+
+ wiphy_warn(priv->hw->wiphy,
+ "Multicast delivery timeout.\n");
+ spin_lock_bh(&priv->ps_state_lock);
+ priv->tx_multicast = priv->aid0_bit_set &&
+ priv->buffered_multicasts;
+ if (priv->tx_multicast)
+ cw1200_bh_wakeup(priv);
+ spin_unlock_bh(&priv->ps_state_lock);
+}
+
+int cw1200_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum ieee80211_ampdu_mlme_action action,
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+ u8 buf_size)
+{
+ /* Aggregation is implemented fully in firmware,
+ * including block ack negotiation. Do not allow
+ * mac80211 stack to do anything: it interferes with
+ * the firmware.
+ */
+
+ /* Note that we still need this function stubbed. */
+ return -ENOTSUPP;
+}
+
+/* ******************************************************************** */
+/* WSM callback */
+void cw1200_suspend_resume(struct cw1200_common *priv,
+ struct wsm_suspend_resume *arg)
+{
+ pr_debug("[AP] %s: %s\n",
+ arg->stop ? "stop" : "start",
+ arg->multicast ? "broadcast" : "unicast");
+
+ if (arg->multicast) {
+ bool cancel_tmo = false;
+ spin_lock_bh(&priv->ps_state_lock);
+ if (arg->stop) {
+ priv->tx_multicast = false;
+ } else {
+ /* Firmware sends this indication every DTIM if there
+ * is a STA in powersave connected. There is no reason
+ * to suspend, following wakeup will consume much more
+ * power than it could be saved.
+ */
+ cw1200_pm_stay_awake(&priv->pm_state,
+ priv->join_dtim_period *
+ (priv->beacon_int + 20) * HZ / 1024);
+ priv->tx_multicast = (priv->aid0_bit_set &&
+ priv->buffered_multicasts);
+ if (priv->tx_multicast) {
+ cancel_tmo = true;
+ cw1200_bh_wakeup(priv);
+ }
+ }
+ spin_unlock_bh(&priv->ps_state_lock);
+ if (cancel_tmo)
+ del_timer_sync(&priv->mcast_timeout);
+ } else {
+ spin_lock_bh(&priv->ps_state_lock);
+ cw1200_ps_notify(priv, arg->link_id, arg->stop);
+ spin_unlock_bh(&priv->ps_state_lock);
+ if (!arg->stop)
+ cw1200_bh_wakeup(priv);
+ }
+ return;
+}
+
+/* ******************************************************************** */
+/* AP privates */
+
+static int cw1200_upload_beacon(struct cw1200_common *priv)
+{
+ int ret = 0;
+ struct ieee80211_mgmt *mgmt;
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_BEACON,
+ };
+
+ u16 tim_offset;
+ u16 tim_len;
+
+ if (priv->mode == NL80211_IFTYPE_STATION ||
+ priv->mode == NL80211_IFTYPE_MONITOR ||
+ priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+ goto done;
+
+ if (priv->vif->p2p)
+ frame.rate = WSM_TRANSMIT_RATE_6;
+
+ frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
+ &tim_offset, &tim_len);
+ if (!frame.skb)
+ return -ENOMEM;
+
+ ret = wsm_set_template_frame(priv, &frame);
+
+ if (ret)
+ goto done;
+
+ /* TODO: Distill probe resp; remove TIM
+ * and any other beacon-specific IEs
+ */
+ mgmt = (void *)frame.skb->data;
+ mgmt->frame_control =
+ __cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_PROBE_RESP);
+
+ frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE;
+ if (priv->vif->p2p) {
+ ret = wsm_set_probe_responder(priv, true);
+ } else {
+ ret = wsm_set_template_frame(priv, &frame);
+ wsm_set_probe_responder(priv, false);
+ }
+
+done:
+ dev_kfree_skb(frame.skb);
+
+ return ret;
+}
+
+static int cw1200_upload_pspoll(struct cw1200_common *priv)
+{
+ int ret = 0;
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_PS_POLL,
+ .rate = 0xFF,
+ };
+
+
+ frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif);
+ if (!frame.skb)
+ return -ENOMEM;
+
+ ret = wsm_set_template_frame(priv, &frame);
+
+ dev_kfree_skb(frame.skb);
+
+ return ret;
+}
+
+static int cw1200_upload_null(struct cw1200_common *priv)
+{
+ int ret = 0;
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_NULL,
+ .rate = 0xFF,
+ };
+
+ frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
+ if (!frame.skb)
+ return -ENOMEM;
+
+ ret = wsm_set_template_frame(priv, &frame);
+
+ dev_kfree_skb(frame.skb);
+
+ return ret;
+}
+
+static int cw1200_upload_qosnull(struct cw1200_common *priv)
+{
+ int ret = 0;
+ /* TODO: This needs to be implemented
+
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_QOS_NULL,
+ .rate = 0xFF,
+ };
+
+ frame.skb = ieee80211_qosnullfunc_get(priv->hw, priv->vif);
+ if (!frame.skb)
+ return -ENOMEM;
+
+ ret = wsm_set_template_frame(priv, &frame);
+
+ dev_kfree_skb(frame.skb);
+
+ */
+ return ret;
+}
+
+static int cw1200_enable_beaconing(struct cw1200_common *priv,
+ bool enable)
+{
+ struct wsm_beacon_transmit transmit = {
+ .enable_beaconing = enable,
+ };
+
+ return wsm_beacon_transmit(priv, &transmit);
+}
+
+static int cw1200_start_ap(struct cw1200_common *priv)
+{
+ int ret;
+ struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+ struct wsm_start start = {
+ .mode = priv->vif->p2p ?
+ WSM_START_MODE_P2P_GO : WSM_START_MODE_AP,
+ .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
+ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G,
+ .channel_number = priv->channel->hw_value,
+ .beacon_interval = conf->beacon_int,
+ .dtim_period = conf->dtim_period,
+ .preamble = conf->use_short_preamble ?
+ WSM_JOIN_PREAMBLE_SHORT :
+ WSM_JOIN_PREAMBLE_LONG,
+ .probe_delay = 100,
+ .basic_rate_set = cw1200_rate_mask_to_wsm(priv,
+ conf->basic_rates),
+ };
+ struct wsm_operational_mode mode = {
+ .power_mode = cw1200_power_mode,
+ .disable_more_flag_usage = true,
+ };
+
+ memset(start.ssid, 0, sizeof(start.ssid));
+ if (!conf->hidden_ssid) {
+ start.ssid_len = conf->ssid_len;
+ memcpy(start.ssid, conf->ssid, start.ssid_len);
+ }
+
+ priv->beacon_int = conf->beacon_int;
+ priv->join_dtim_period = conf->dtim_period;
+
+ memset(&priv->link_id_db, 0, sizeof(priv->link_id_db));
+
+ pr_debug("[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n",
+ start.channel_number, start.band,
+ start.beacon_interval, start.dtim_period,
+ start.basic_rate_set,
+ start.ssid_len, start.ssid);
+ ret = wsm_start(priv, &start);
+ if (!ret)
+ ret = cw1200_upload_keys(priv);
+ if (!ret && priv->vif->p2p) {
+ pr_debug("[AP] Setting p2p powersave configuration.\n");
+ wsm_set_p2p_ps_modeinfo(priv, &priv->p2p_ps_modeinfo);
+ }
+ if (!ret) {
+ wsm_set_block_ack_policy(priv, 0, 0);
+ priv->join_status = CW1200_JOIN_STATUS_AP;
+ cw1200_update_filtering(priv);
+ }
+ wsm_set_operational_mode(priv, &mode);
+ return ret;
+}
+
+static int cw1200_update_beaconing(struct cw1200_common *priv)
+{
+ struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+ struct wsm_reset reset = {
+ .link_id = 0,
+ .reset_statistics = true,
+ };
+
+ if (priv->mode == NL80211_IFTYPE_AP) {
+ /* TODO: check if changed channel, band */
+ if (priv->join_status != CW1200_JOIN_STATUS_AP ||
+ priv->beacon_int != conf->beacon_int) {
+ pr_debug("ap restarting\n");
+ wsm_lock_tx(priv);
+ if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE)
+ wsm_reset(priv, &reset);
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ cw1200_start_ap(priv);
+ wsm_unlock_tx(priv);
+ } else
+ pr_debug("ap started join_status: %d\n",
+ priv->join_status);
+ }
+ return 0;
+}
diff --git a/drivers/net/wireless/cw1200/sta.h b/drivers/net/wireless/cw1200/sta.h
new file mode 100644
index 000000000000..35babb62cc6a
--- /dev/null
+++ b/drivers/net/wireless/cw1200/sta.h
@@ -0,0 +1,123 @@
+/*
+ * Mac80211 STA interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 STA_H_INCLUDED
+#define STA_H_INCLUDED
+
+/* ******************************************************************** */
+/* mac80211 API */
+
+int cw1200_start(struct ieee80211_hw *dev);
+void cw1200_stop(struct ieee80211_hw *dev);
+int cw1200_add_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif);
+void cw1200_remove_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif);
+int cw1200_change_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype new_type,
+ bool p2p);
+int cw1200_config(struct ieee80211_hw *dev, u32 changed);
+void cw1200_configure_filter(struct ieee80211_hw *dev,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast);
+int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+ u16 queue, const struct ieee80211_tx_queue_params *params);
+int cw1200_get_stats(struct ieee80211_hw *dev,
+ struct ieee80211_low_level_stats *stats);
+int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key);
+
+int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
+
+void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
+
+u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
+ struct netdev_hw_addr_list *mc_list);
+
+int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg);
+
+/* ******************************************************************** */
+/* WSM callbacks */
+
+void cw1200_join_complete_cb(struct cw1200_common *priv,
+ struct wsm_join_complete *arg);
+
+/* ******************************************************************** */
+/* WSM events */
+
+void cw1200_free_event_queue(struct cw1200_common *priv);
+void cw1200_event_handler(struct work_struct *work);
+void cw1200_bss_loss_work(struct work_struct *work);
+void cw1200_bss_params_work(struct work_struct *work);
+void cw1200_keep_alive_work(struct work_struct *work);
+void cw1200_tx_failure_work(struct work_struct *work);
+
+void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, int init, int good,
+ int bad);
+static inline void cw1200_cqm_bssloss_sm(struct cw1200_common *priv,
+ int init, int good, int bad)
+{
+ spin_lock(&priv->bss_loss_lock);
+ __cw1200_cqm_bssloss_sm(priv, init, good, bad);
+ spin_unlock(&priv->bss_loss_lock);
+}
+
+/* ******************************************************************** */
+/* Internal API */
+
+int cw1200_setup_mac(struct cw1200_common *priv);
+void cw1200_join_timeout(struct work_struct *work);
+void cw1200_unjoin_work(struct work_struct *work);
+void cw1200_join_complete_work(struct work_struct *work);
+void cw1200_wep_key_work(struct work_struct *work);
+void cw1200_update_listening(struct cw1200_common *priv, bool enabled);
+void cw1200_update_filtering(struct cw1200_common *priv);
+void cw1200_update_filtering_work(struct work_struct *work);
+void cw1200_set_beacon_wakeup_period_work(struct work_struct *work);
+int cw1200_enable_listening(struct cw1200_common *priv);
+int cw1200_disable_listening(struct cw1200_common *priv);
+int cw1200_set_uapsd_param(struct cw1200_common *priv,
+ const struct wsm_edca_params *arg);
+void cw1200_ba_work(struct work_struct *work);
+void cw1200_ba_timer(unsigned long arg);
+
+/* AP stuffs */
+int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
+ bool set);
+int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+ enum sta_notify_cmd notify_cmd,
+ struct ieee80211_sta *sta);
+void cw1200_bss_info_changed(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed);
+int cw1200_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum ieee80211_ampdu_mlme_action action,
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+ u8 buf_size);
+
+void cw1200_suspend_resume(struct cw1200_common *priv,
+ struct wsm_suspend_resume *arg);
+void cw1200_set_tim_work(struct work_struct *work);
+void cw1200_set_cts_work(struct work_struct *work);
+void cw1200_multicast_start_work(struct work_struct *work);
+void cw1200_multicast_stop_work(struct work_struct *work);
+void cw1200_mcast_timeout(unsigned long arg);
+
+#endif
diff --git a/drivers/net/wireless/cw1200/txrx.c b/drivers/net/wireless/cw1200/txrx.c
new file mode 100644
index 000000000000..44ca10cb0d39
--- /dev/null
+++ b/drivers/net/wireless/cw1200/txrx.c
@@ -0,0 +1,1474 @@
+/*
+ * Datapath implementation for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <net/mac80211.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "cw1200.h"
+#include "wsm.h"
+#include "bh.h"
+#include "sta.h"
+#include "debug.h"
+
+#define CW1200_INVALID_RATE_ID (0xFF)
+
+static int cw1200_handle_action_rx(struct cw1200_common *priv,
+ struct sk_buff *skb);
+static const struct ieee80211_rate *
+cw1200_get_tx_rate(const struct cw1200_common *priv,
+ const struct ieee80211_tx_rate *rate);
+
+/* ******************************************************************** */
+/* TX queue lock / unlock */
+
+static inline void cw1200_tx_queues_lock(struct cw1200_common *priv)
+{
+ int i;
+ for (i = 0; i < 4; ++i)
+ cw1200_queue_lock(&priv->tx_queue[i]);
+}
+
+static inline void cw1200_tx_queues_unlock(struct cw1200_common *priv)
+{
+ int i;
+ for (i = 0; i < 4; ++i)
+ cw1200_queue_unlock(&priv->tx_queue[i]);
+}
+
+/* ******************************************************************** */
+/* TX policy cache implementation */
+
+static void tx_policy_dump(struct tx_policy *policy)
+{
+ pr_debug("[TX policy] %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n",
+ policy->raw[0] & 0x0F, policy->raw[0] >> 4,
+ policy->raw[1] & 0x0F, policy->raw[1] >> 4,
+ policy->raw[2] & 0x0F, policy->raw[2] >> 4,
+ policy->raw[3] & 0x0F, policy->raw[3] >> 4,
+ policy->raw[4] & 0x0F, policy->raw[4] >> 4,
+ policy->raw[5] & 0x0F, policy->raw[5] >> 4,
+ policy->raw[6] & 0x0F, policy->raw[6] >> 4,
+ policy->raw[7] & 0x0F, policy->raw[7] >> 4,
+ policy->raw[8] & 0x0F, policy->raw[8] >> 4,
+ policy->raw[9] & 0x0F, policy->raw[9] >> 4,
+ policy->raw[10] & 0x0F, policy->raw[10] >> 4,
+ policy->raw[11] & 0x0F, policy->raw[11] >> 4,
+ policy->defined);
+}
+
+static void tx_policy_build(const struct cw1200_common *priv,
+ /* [out] */ struct tx_policy *policy,
+ struct ieee80211_tx_rate *rates, size_t count)
+{
+ int i, j;
+ unsigned limit = priv->short_frame_max_tx_count;
+ unsigned total = 0;
+ BUG_ON(rates[0].idx < 0);
+ memset(policy, 0, sizeof(*policy));
+
+ /* Sort rates in descending order. */
+ for (i = 1; i < count; ++i) {
+ if (rates[i].idx < 0) {
+ count = i;
+ break;
+ }
+ if (rates[i].idx > rates[i - 1].idx) {
+ struct ieee80211_tx_rate tmp = rates[i - 1];
+ rates[i - 1] = rates[i];
+ rates[i] = tmp;
+ }
+ }
+
+ /* Eliminate duplicates. */
+ total = rates[0].count;
+ for (i = 0, j = 1; j < count; ++j) {
+ if (rates[j].idx == rates[i].idx) {
+ rates[i].count += rates[j].count;
+ } else if (rates[j].idx > rates[i].idx) {
+ break;
+ } else {
+ ++i;
+ if (i != j)
+ rates[i] = rates[j];
+ }
+ total += rates[j].count;
+ }
+ count = i + 1;
+
+ /* Re-fill policy trying to keep every requested rate and with
+ * respect to the global max tx retransmission count.
+ */
+ if (limit < count)
+ limit = count;
+ if (total > limit) {
+ for (i = 0; i < count; ++i) {
+ int left = count - i - 1;
+ if (rates[i].count > limit - left)
+ rates[i].count = limit - left;
+ limit -= rates[i].count;
+ }
+ }
+
+ /* HACK!!! Device has problems (at least) switching from
+ * 54Mbps CTS to 1Mbps. This switch takes enormous amount
+ * of time (100-200 ms), leading to valuable throughput drop.
+ * As a workaround, additional g-rates are injected to the
+ * policy.
+ */
+ if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) &&
+ rates[0].idx > 4 && rates[0].count > 2 &&
+ rates[1].idx < 2) {
+ int mid_rate = (rates[0].idx + 4) >> 1;
+
+ /* Decrease number of retries for the initial rate */
+ rates[0].count -= 2;
+
+ if (mid_rate != 4) {
+ /* Keep fallback rate at 1Mbps. */
+ rates[3] = rates[1];
+
+ /* Inject 1 transmission on lowest g-rate */
+ rates[2].idx = 4;
+ rates[2].count = 1;
+ rates[2].flags = rates[1].flags;
+
+ /* Inject 1 transmission on mid-rate */
+ rates[1].idx = mid_rate;
+ rates[1].count = 1;
+
+ /* Fallback to 1 Mbps is a really bad thing,
+ * so let's try to increase probability of
+ * successful transmission on the lowest g rate
+ * even more
+ */
+ if (rates[0].count >= 3) {
+ --rates[0].count;
+ ++rates[2].count;
+ }
+
+ /* Adjust amount of rates defined */
+ count += 2;
+ } else {
+ /* Keep fallback rate at 1Mbps. */
+ rates[2] = rates[1];
+
+ /* Inject 2 transmissions on lowest g-rate */
+ rates[1].idx = 4;
+ rates[1].count = 2;
+
+ /* Adjust amount of rates defined */
+ count += 1;
+ }
+ }
+
+ policy->defined = cw1200_get_tx_rate(priv, &rates[0])->hw_value + 1;
+
+ for (i = 0; i < count; ++i) {
+ register unsigned rateid, off, shift, retries;
+
+ rateid = cw1200_get_tx_rate(priv, &rates[i])->hw_value;
+ off = rateid >> 3; /* eq. rateid / 8 */
+ shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */
+
+ retries = rates[i].count;
+ if (retries > 0x0F) {
+ rates[i].count = 0x0f;
+ retries = 0x0F;
+ }
+ policy->tbl[off] |= __cpu_to_le32(retries << shift);
+ policy->retry_count += retries;
+ }
+
+ pr_debug("[TX policy] Policy (%zu): %d:%d, %d:%d, %d:%d, %d:%d\n",
+ count,
+ rates[0].idx, rates[0].count,
+ rates[1].idx, rates[1].count,
+ rates[2].idx, rates[2].count,
+ rates[3].idx, rates[3].count);
+}
+
+static inline bool tx_policy_is_equal(const struct tx_policy *wanted,
+ const struct tx_policy *cached)
+{
+ size_t count = wanted->defined >> 1;
+ if (wanted->defined > cached->defined)
+ return false;
+ if (count) {
+ if (memcmp(wanted->raw, cached->raw, count))
+ return false;
+ }
+ if (wanted->defined & 1) {
+ if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F))
+ return false;
+ }
+ return true;
+}
+
+static int tx_policy_find(struct tx_policy_cache *cache,
+ const struct tx_policy *wanted)
+{
+ /* O(n) complexity. Not so good, but there's only 8 entries in
+ * the cache.
+ * Also lru helps to reduce search time.
+ */
+ struct tx_policy_cache_entry *it;
+ /* First search for policy in "used" list */
+ list_for_each_entry(it, &cache->used, link) {
+ if (tx_policy_is_equal(wanted, &it->policy))
+ return it - cache->cache;
+ }
+ /* Then - in "free list" */
+ list_for_each_entry(it, &cache->free, link) {
+ if (tx_policy_is_equal(wanted, &it->policy))
+ return it - cache->cache;
+ }
+ return -1;
+}
+
+static inline void tx_policy_use(struct tx_policy_cache *cache,
+ struct tx_policy_cache_entry *entry)
+{
+ ++entry->policy.usage_count;
+ list_move(&entry->link, &cache->used);
+}
+
+static inline int tx_policy_release(struct tx_policy_cache *cache,
+ struct tx_policy_cache_entry *entry)
+{
+ int ret = --entry->policy.usage_count;
+ if (!ret)
+ list_move(&entry->link, &cache->free);
+ return ret;
+}
+
+void tx_policy_clean(struct cw1200_common *priv)
+{
+ int idx, locked;
+ struct tx_policy_cache *cache = &priv->tx_policy_cache;
+ struct tx_policy_cache_entry *entry;
+
+ cw1200_tx_queues_lock(priv);
+ spin_lock_bh(&cache->lock);
+ locked = list_empty(&cache->free);
+
+ for (idx = 0; idx < TX_POLICY_CACHE_SIZE; idx++) {
+ entry = &cache->cache[idx];
+ /* Policy usage count should be 0 at this time as all queues
+ should be empty
+ */
+ if (WARN_ON(entry->policy.usage_count)) {
+ entry->policy.usage_count = 0;
+ list_move(&entry->link, &cache->free);
+ }
+ memset(&entry->policy, 0, sizeof(entry->policy));
+ }
+ if (locked)
+ cw1200_tx_queues_unlock(priv);
+
+ cw1200_tx_queues_unlock(priv);
+ spin_unlock_bh(&cache->lock);
+}
+
+/* ******************************************************************** */
+/* External TX policy cache API */
+
+void tx_policy_init(struct cw1200_common *priv)
+{
+ struct tx_policy_cache *cache = &priv->tx_policy_cache;
+ int i;
+
+ memset(cache, 0, sizeof(*cache));
+
+ spin_lock_init(&cache->lock);
+ INIT_LIST_HEAD(&cache->used);
+ INIT_LIST_HEAD(&cache->free);
+
+ for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i)
+ list_add(&cache->cache[i].link, &cache->free);
+}
+
+static int tx_policy_get(struct cw1200_common *priv,
+ struct ieee80211_tx_rate *rates,
+ size_t count, bool *renew)
+{
+ int idx;
+ struct tx_policy_cache *cache = &priv->tx_policy_cache;
+ struct tx_policy wanted;
+
+ tx_policy_build(priv, &wanted, rates, count);
+
+ spin_lock_bh(&cache->lock);
+ if (WARN_ON_ONCE(list_empty(&cache->free))) {
+ spin_unlock_bh(&cache->lock);
+ return CW1200_INVALID_RATE_ID;
+ }
+ idx = tx_policy_find(cache, &wanted);
+ if (idx >= 0) {
+ pr_debug("[TX policy] Used TX policy: %d\n", idx);
+ *renew = false;
+ } else {
+ struct tx_policy_cache_entry *entry;
+ *renew = true;
+ /* If policy is not found create a new one
+ * using the oldest entry in "free" list
+ */
+ entry = list_entry(cache->free.prev,
+ struct tx_policy_cache_entry, link);
+ entry->policy = wanted;
+ idx = entry - cache->cache;
+ pr_debug("[TX policy] New TX policy: %d\n", idx);
+ tx_policy_dump(&entry->policy);
+ }
+ tx_policy_use(cache, &cache->cache[idx]);
+ if (list_empty(&cache->free)) {
+ /* Lock TX queues. */
+ cw1200_tx_queues_lock(priv);
+ }
+ spin_unlock_bh(&cache->lock);
+ return idx;
+}
+
+static void tx_policy_put(struct cw1200_common *priv, int idx)
+{
+ int usage, locked;
+ struct tx_policy_cache *cache = &priv->tx_policy_cache;
+
+ spin_lock_bh(&cache->lock);
+ locked = list_empty(&cache->free);
+ usage = tx_policy_release(cache, &cache->cache[idx]);
+ if (locked && !usage) {
+ /* Unlock TX queues. */
+ cw1200_tx_queues_unlock(priv);
+ }
+ spin_unlock_bh(&cache->lock);
+}
+
+static int tx_policy_upload(struct cw1200_common *priv)
+{
+ struct tx_policy_cache *cache = &priv->tx_policy_cache;
+ int i;
+ struct wsm_set_tx_rate_retry_policy arg = {
+ .num = 0,
+ };
+ spin_lock_bh(&cache->lock);
+
+ /* Upload only modified entries. */
+ for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) {
+ struct tx_policy *src = &cache->cache[i].policy;
+ if (src->retry_count && !src->uploaded) {
+ struct wsm_tx_rate_retry_policy *dst =
+ &arg.tbl[arg.num];
+ dst->index = i;
+ dst->short_retries = priv->short_frame_max_tx_count;
+ dst->long_retries = priv->long_frame_max_tx_count;
+
+ dst->flags = WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED |
+ WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT;
+ memcpy(dst->rate_count_indices, src->tbl,
+ sizeof(dst->rate_count_indices));
+ src->uploaded = 1;
+ ++arg.num;
+ }
+ }
+ spin_unlock_bh(&cache->lock);
+ cw1200_debug_tx_cache_miss(priv);
+ pr_debug("[TX policy] Upload %d policies\n", arg.num);
+ return wsm_set_tx_rate_retry_policy(priv, &arg);
+}
+
+void tx_policy_upload_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, tx_policy_upload_work);
+
+ pr_debug("[TX] TX policy upload.\n");
+ tx_policy_upload(priv);
+
+ wsm_unlock_tx(priv);
+ cw1200_tx_queues_unlock(priv);
+}
+
+/* ******************************************************************** */
+/* cw1200 TX implementation */
+
+struct cw1200_txinfo {
+ struct sk_buff *skb;
+ unsigned queue;
+ struct ieee80211_tx_info *tx_info;
+ const struct ieee80211_rate *rate;
+ struct ieee80211_hdr *hdr;
+ size_t hdrlen;
+ const u8 *da;
+ struct cw1200_sta_priv *sta_priv;
+ struct ieee80211_sta *sta;
+ struct cw1200_txpriv txpriv;
+};
+
+u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates)
+{
+ u32 ret = 0;
+ int i;
+ for (i = 0; i < 32; ++i) {
+ if (rates & BIT(i))
+ ret |= BIT(priv->rates[i].hw_value);
+ }
+ return ret;
+}
+
+static const struct ieee80211_rate *
+cw1200_get_tx_rate(const struct cw1200_common *priv,
+ const struct ieee80211_tx_rate *rate)
+{
+ if (rate->idx < 0)
+ return NULL;
+ if (rate->flags & IEEE80211_TX_RC_MCS)
+ return &priv->mcs_rates[rate->idx];
+ return &priv->hw->wiphy->bands[priv->channel->band]->
+ bitrates[rate->idx];
+}
+
+static int
+cw1200_tx_h_calc_link_ids(struct cw1200_common *priv,
+ struct cw1200_txinfo *t)
+{
+ if (t->sta && t->sta_priv->link_id)
+ t->txpriv.raw_link_id =
+ t->txpriv.link_id =
+ t->sta_priv->link_id;
+ else if (priv->mode != NL80211_IFTYPE_AP)
+ t->txpriv.raw_link_id =
+ t->txpriv.link_id = 0;
+ else if (is_multicast_ether_addr(t->da)) {
+ if (priv->enable_beacon) {
+ t->txpriv.raw_link_id = 0;
+ t->txpriv.link_id = CW1200_LINK_ID_AFTER_DTIM;
+ } else {
+ t->txpriv.raw_link_id = 0;
+ t->txpriv.link_id = 0;
+ }
+ } else {
+ t->txpriv.link_id = cw1200_find_link_id(priv, t->da);
+ if (!t->txpriv.link_id)
+ t->txpriv.link_id = cw1200_alloc_link_id(priv, t->da);
+ if (!t->txpriv.link_id) {
+ wiphy_err(priv->hw->wiphy,
+ "No more link IDs available.\n");
+ return -ENOENT;
+ }
+ t->txpriv.raw_link_id = t->txpriv.link_id;
+ }
+ if (t->txpriv.raw_link_id)
+ priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp =
+ jiffies;
+ if (t->sta && (t->sta->uapsd_queues & BIT(t->queue)))
+ t->txpriv.link_id = CW1200_LINK_ID_UAPSD;
+ return 0;
+}
+
+static void
+cw1200_tx_h_pm(struct cw1200_common *priv,
+ struct cw1200_txinfo *t)
+{
+ if (ieee80211_is_auth(t->hdr->frame_control)) {
+ u32 mask = ~BIT(t->txpriv.raw_link_id);
+ spin_lock_bh(&priv->ps_state_lock);
+ priv->sta_asleep_mask &= mask;
+ priv->pspoll_mask &= mask;
+ spin_unlock_bh(&priv->ps_state_lock);
+ }
+}
+
+static void
+cw1200_tx_h_calc_tid(struct cw1200_common *priv,
+ struct cw1200_txinfo *t)
+{
+ if (ieee80211_is_data_qos(t->hdr->frame_control)) {
+ u8 *qos = ieee80211_get_qos_ctl(t->hdr);
+ t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK;
+ } else if (ieee80211_is_data(t->hdr->frame_control)) {
+ t->txpriv.tid = 0;
+ }
+}
+
+static int
+cw1200_tx_h_crypt(struct cw1200_common *priv,
+ struct cw1200_txinfo *t)
+{
+ if (!t->tx_info->control.hw_key ||
+ !ieee80211_has_protected(t->hdr->frame_control))
+ return 0;
+
+ t->hdrlen += t->tx_info->control.hw_key->iv_len;
+ skb_put(t->skb, t->tx_info->control.hw_key->icv_len);
+
+ if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
+ skb_put(t->skb, 8); /* MIC space */
+
+ return 0;
+}
+
+static int
+cw1200_tx_h_align(struct cw1200_common *priv,
+ struct cw1200_txinfo *t,
+ u8 *flags)
+{
+ size_t offset = (size_t)t->skb->data & 3;
+
+ if (!offset)
+ return 0;
+
+ if (offset & 1) {
+ wiphy_err(priv->hw->wiphy,
+ "Bug: attempt to transmit a frame with wrong alignment: %zu\n",
+ offset);
+ return -EINVAL;
+ }
+
+ if (skb_headroom(t->skb) < offset) {
+ wiphy_err(priv->hw->wiphy,
+ "Bug: no space allocated for DMA alignment. headroom: %d\n",
+ skb_headroom(t->skb));
+ return -ENOMEM;
+ }
+ skb_push(t->skb, offset);
+ t->hdrlen += offset;
+ t->txpriv.offset += offset;
+ *flags |= WSM_TX_2BYTES_SHIFT;
+ cw1200_debug_tx_align(priv);
+ return 0;
+}
+
+static int
+cw1200_tx_h_action(struct cw1200_common *priv,
+ struct cw1200_txinfo *t)
+{
+ struct ieee80211_mgmt *mgmt =
+ (struct ieee80211_mgmt *)t->hdr;
+ if (ieee80211_is_action(t->hdr->frame_control) &&
+ mgmt->u.action.category == WLAN_CATEGORY_BACK)
+ return 1;
+ else
+ return 0;
+}
+
+/* Add WSM header */
+static struct wsm_tx *
+cw1200_tx_h_wsm(struct cw1200_common *priv,
+ struct cw1200_txinfo *t)
+{
+ struct wsm_tx *wsm;
+
+ if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) {
+ wiphy_err(priv->hw->wiphy,
+ "Bug: no space allocated for WSM header. headroom: %d\n",
+ skb_headroom(t->skb));
+ return NULL;
+ }
+
+ wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx));
+ t->txpriv.offset += sizeof(struct wsm_tx);
+ memset(wsm, 0, sizeof(*wsm));
+ wsm->hdr.len = __cpu_to_le16(t->skb->len);
+ wsm->hdr.id = __cpu_to_le16(0x0004);
+ wsm->queue_id = wsm_queue_id_to_wsm(t->queue);
+ return wsm;
+}
+
+/* BT Coex specific handling */
+static void
+cw1200_tx_h_bt(struct cw1200_common *priv,
+ struct cw1200_txinfo *t,
+ struct wsm_tx *wsm)
+{
+ u8 priority = 0;
+
+ if (!priv->bt_present)
+ return;
+
+ if (ieee80211_is_nullfunc(t->hdr->frame_control)) {
+ priority = WSM_EPTA_PRIORITY_MGT;
+ } else if (ieee80211_is_data(t->hdr->frame_control)) {
+ /* Skip LLC SNAP header (+6) */
+ u8 *payload = &t->skb->data[t->hdrlen];
+ u16 *ethertype = (u16 *)&payload[6];
+ if (*ethertype == __be16_to_cpu(ETH_P_PAE))
+ priority = WSM_EPTA_PRIORITY_EAPOL;
+ } else if (ieee80211_is_assoc_req(t->hdr->frame_control) ||
+ ieee80211_is_reassoc_req(t->hdr->frame_control)) {
+ struct ieee80211_mgmt *mgt_frame =
+ (struct ieee80211_mgmt *)t->hdr;
+
+ if (mgt_frame->u.assoc_req.listen_interval <
+ priv->listen_interval) {
+ pr_debug("Modified Listen Interval to %d from %d\n",
+ priv->listen_interval,
+ mgt_frame->u.assoc_req.listen_interval);
+ /* Replace listen interval derieved from
+ * the one read from SDD
+ */
+ mgt_frame->u.assoc_req.listen_interval =
+ priv->listen_interval;
+ }
+ }
+
+ if (!priority) {
+ if (ieee80211_is_action(t->hdr->frame_control))
+ priority = WSM_EPTA_PRIORITY_ACTION;
+ else if (ieee80211_is_mgmt(t->hdr->frame_control))
+ priority = WSM_EPTA_PRIORITY_MGT;
+ else if ((wsm->queue_id == WSM_QUEUE_VOICE))
+ priority = WSM_EPTA_PRIORITY_VOICE;
+ else if ((wsm->queue_id == WSM_QUEUE_VIDEO))
+ priority = WSM_EPTA_PRIORITY_VIDEO;
+ else
+ priority = WSM_EPTA_PRIORITY_DATA;
+ }
+
+ pr_debug("[TX] EPTA priority %d.\n", priority);
+
+ wsm->flags |= priority << 1;
+}
+
+static int
+cw1200_tx_h_rate_policy(struct cw1200_common *priv,
+ struct cw1200_txinfo *t,
+ struct wsm_tx *wsm)
+{
+ bool tx_policy_renew = false;
+
+ t->txpriv.rate_id = tx_policy_get(priv,
+ t->tx_info->control.rates, IEEE80211_TX_MAX_RATES,
+ &tx_policy_renew);
+ if (t->txpriv.rate_id == CW1200_INVALID_RATE_ID)
+ return -EFAULT;
+
+ wsm->flags |= t->txpriv.rate_id << 4;
+
+ t->rate = cw1200_get_tx_rate(priv,
+ &t->tx_info->control.rates[0]),
+ wsm->max_tx_rate = t->rate->hw_value;
+ if (t->rate->flags & IEEE80211_TX_RC_MCS) {
+ if (cw1200_ht_greenfield(&priv->ht_info))
+ wsm->ht_tx_parameters |=
+ __cpu_to_le32(WSM_HT_TX_GREENFIELD);
+ else
+ wsm->ht_tx_parameters |=
+ __cpu_to_le32(WSM_HT_TX_MIXED);
+ }
+
+ if (tx_policy_renew) {
+ pr_debug("[TX] TX policy renew.\n");
+ /* It's not so optimal to stop TX queues every now and then.
+ * Better to reimplement task scheduling with
+ * a counter. TODO.
+ */
+ wsm_lock_tx_async(priv);
+ cw1200_tx_queues_lock(priv);
+ if (queue_work(priv->workqueue,
+ &priv->tx_policy_upload_work) <= 0) {
+ cw1200_tx_queues_unlock(priv);
+ wsm_unlock_tx(priv);
+ }
+ }
+ return 0;
+}
+
+static bool
+cw1200_tx_h_pm_state(struct cw1200_common *priv,
+ struct cw1200_txinfo *t)
+{
+ int was_buffered = 1;
+
+ if (t->txpriv.link_id == CW1200_LINK_ID_AFTER_DTIM &&
+ !priv->buffered_multicasts) {
+ priv->buffered_multicasts = true;
+ if (priv->sta_asleep_mask)
+ queue_work(priv->workqueue,
+ &priv->multicast_start_work);
+ }
+
+ if (t->txpriv.raw_link_id && t->txpriv.tid < CW1200_MAX_TID)
+ was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1].buffered[t->txpriv.tid]++;
+
+ return !was_buffered;
+}
+
+/* ******************************************************************** */
+
+void cw1200_tx(struct ieee80211_hw *dev,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+{
+ struct cw1200_common *priv = dev->priv;
+ struct cw1200_txinfo t = {
+ .skb = skb,
+ .queue = skb_get_queue_mapping(skb),
+ .tx_info = IEEE80211_SKB_CB(skb),
+ .hdr = (struct ieee80211_hdr *)skb->data,
+ .txpriv.tid = CW1200_MAX_TID,
+ .txpriv.rate_id = CW1200_INVALID_RATE_ID,
+ };
+ struct ieee80211_sta *sta;
+ struct wsm_tx *wsm;
+ bool tid_update = 0;
+ u8 flags = 0;
+ int ret;
+
+ if (priv->bh_error)
+ goto drop;
+
+ t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control);
+ t.da = ieee80211_get_DA(t.hdr);
+ if (control) {
+ t.sta = control->sta;
+ t.sta_priv = (struct cw1200_sta_priv *)&t.sta->drv_priv;
+ }
+
+ if (WARN_ON(t.queue >= 4))
+ goto drop;
+
+ ret = cw1200_tx_h_calc_link_ids(priv, &t);
+ if (ret)
+ goto drop;
+
+ pr_debug("[TX] TX %d bytes (queue: %d, link_id: %d (%d)).\n",
+ skb->len, t.queue, t.txpriv.link_id,
+ t.txpriv.raw_link_id);
+
+ cw1200_tx_h_pm(priv, &t);
+ cw1200_tx_h_calc_tid(priv, &t);
+ ret = cw1200_tx_h_crypt(priv, &t);
+ if (ret)
+ goto drop;
+ ret = cw1200_tx_h_align(priv, &t, &flags);
+ if (ret)
+ goto drop;
+ ret = cw1200_tx_h_action(priv, &t);
+ if (ret)
+ goto drop;
+ wsm = cw1200_tx_h_wsm(priv, &t);
+ if (!wsm) {
+ ret = -ENOMEM;
+ goto drop;
+ }
+ wsm->flags |= flags;
+ cw1200_tx_h_bt(priv, &t, wsm);
+ ret = cw1200_tx_h_rate_policy(priv, &t, wsm);
+ if (ret)
+ goto drop;
+
+ rcu_read_lock();
+ sta = rcu_dereference(t.sta);
+
+ spin_lock_bh(&priv->ps_state_lock);
+ {
+ tid_update = cw1200_tx_h_pm_state(priv, &t);
+ BUG_ON(cw1200_queue_put(&priv->tx_queue[t.queue],
+ t.skb, &t.txpriv));
+ }
+ spin_unlock_bh(&priv->ps_state_lock);
+
+ if (tid_update && sta)
+ ieee80211_sta_set_buffered(sta, t.txpriv.tid, true);
+
+ rcu_read_unlock();
+
+ cw1200_bh_wakeup(priv);
+
+ return;
+
+drop:
+ cw1200_skb_dtor(priv, skb, &t.txpriv);
+ return;
+}
+
+/* ******************************************************************** */
+
+static int cw1200_handle_action_rx(struct cw1200_common *priv,
+ struct sk_buff *skb)
+{
+ struct ieee80211_mgmt *mgmt = (void *)skb->data;
+
+ /* Filter block ACK negotiation: fully controlled by firmware */
+ if (mgmt->u.action.category == WLAN_CATEGORY_BACK)
+ return 1;
+
+ return 0;
+}
+
+static int cw1200_handle_pspoll(struct cw1200_common *priv,
+ struct sk_buff *skb)
+{
+ struct ieee80211_sta *sta;
+ struct ieee80211_pspoll *pspoll = (struct ieee80211_pspoll *)skb->data;
+ int link_id = 0;
+ u32 pspoll_mask = 0;
+ int drop = 1;
+ int i;
+
+ if (priv->join_status != CW1200_JOIN_STATUS_AP)
+ goto done;
+ if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN))
+ goto done;
+
+ rcu_read_lock();
+ sta = ieee80211_find_sta(priv->vif, pspoll->ta);
+ if (sta) {
+ struct cw1200_sta_priv *sta_priv;
+ sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv;
+ link_id = sta_priv->link_id;
+ pspoll_mask = BIT(sta_priv->link_id);
+ }
+ rcu_read_unlock();
+ if (!link_id)
+ goto done;
+
+ priv->pspoll_mask |= pspoll_mask;
+ drop = 0;
+
+ /* Do not report pspols if data for given link id is queued already. */
+ for (i = 0; i < 4; ++i) {
+ if (cw1200_queue_get_num_queued(&priv->tx_queue[i],
+ pspoll_mask)) {
+ cw1200_bh_wakeup(priv);
+ drop = 1;
+ break;
+ }
+ }
+ pr_debug("[RX] PSPOLL: %s\n", drop ? "local" : "fwd");
+done:
+ return drop;
+}
+
+/* ******************************************************************** */
+
+void cw1200_tx_confirm_cb(struct cw1200_common *priv,
+ int link_id,
+ struct wsm_tx_confirm *arg)
+{
+ u8 queue_id = cw1200_queue_get_queue_id(arg->packet_id);
+ struct cw1200_queue *queue = &priv->tx_queue[queue_id];
+ struct sk_buff *skb;
+ const struct cw1200_txpriv *txpriv;
+
+ pr_debug("[TX] TX confirm: %d, %d.\n",
+ arg->status, arg->ack_failures);
+
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
+ /* STA is stopped. */
+ return;
+ }
+
+ if (WARN_ON(queue_id >= 4))
+ return;
+
+ if (arg->status)
+ pr_debug("TX failed: %d.\n", arg->status);
+
+ if ((arg->status == WSM_REQUEUE) &&
+ (arg->flags & WSM_TX_STATUS_REQUEUE)) {
+ /* "Requeue" means "implicit suspend" */
+ struct wsm_suspend_resume suspend = {
+ .link_id = link_id,
+ .stop = 1,
+ .multicast = !link_id,
+ };
+ cw1200_suspend_resume(priv, &suspend);
+ wiphy_warn(priv->hw->wiphy, "Requeue for link_id %d (try %d). STAs asleep: 0x%.8X\n",
+ link_id,
+ cw1200_queue_get_generation(arg->packet_id) + 1,
+ priv->sta_asleep_mask);
+ cw1200_queue_requeue(queue, arg->packet_id);
+ spin_lock_bh(&priv->ps_state_lock);
+ if (!link_id) {
+ priv->buffered_multicasts = true;
+ if (priv->sta_asleep_mask) {
+ queue_work(priv->workqueue,
+ &priv->multicast_start_work);
+ }
+ }
+ spin_unlock_bh(&priv->ps_state_lock);
+ } else if (!cw1200_queue_get_skb(queue, arg->packet_id,
+ &skb, &txpriv)) {
+ struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb);
+ int tx_count = arg->ack_failures;
+ u8 ht_flags = 0;
+ int i;
+
+ if (cw1200_ht_greenfield(&priv->ht_info))
+ ht_flags |= IEEE80211_TX_RC_GREEN_FIELD;
+
+ spin_lock(&priv->bss_loss_lock);
+ if (priv->bss_loss_state &&
+ arg->packet_id == priv->bss_loss_confirm_id) {
+ if (arg->status) {
+ /* Recovery failed */
+ __cw1200_cqm_bssloss_sm(priv, 0, 0, 1);
+ } else {
+ /* Recovery succeeded */
+ __cw1200_cqm_bssloss_sm(priv, 0, 1, 0);
+ }
+ }
+ spin_unlock(&priv->bss_loss_lock);
+
+ if (!arg->status) {
+ tx->flags |= IEEE80211_TX_STAT_ACK;
+ ++tx_count;
+ cw1200_debug_txed(priv);
+ if (arg->flags & WSM_TX_STATUS_AGGREGATION) {
+ /* Do not report aggregation to mac80211:
+ * it confuses minstrel a lot.
+ */
+ /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */
+ cw1200_debug_txed_agg(priv);
+ }
+ } else {
+ if (tx_count)
+ ++tx_count;
+ }
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) {
+ if (tx->status.rates[i].count >= tx_count) {
+ tx->status.rates[i].count = tx_count;
+ break;
+ }
+ tx_count -= tx->status.rates[i].count;
+ if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS)
+ tx->status.rates[i].flags |= ht_flags;
+ }
+
+ for (++i; i < IEEE80211_TX_MAX_RATES; ++i) {
+ tx->status.rates[i].count = 0;
+ tx->status.rates[i].idx = -1;
+ }
+
+ /* Pull off any crypto trailers that we added on */
+ if (tx->control.hw_key) {
+ skb_trim(skb, skb->len - tx->control.hw_key->icv_len);
+ if (tx->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
+ skb_trim(skb, skb->len - 8); /* MIC space */
+ }
+ cw1200_queue_remove(queue, arg->packet_id);
+ }
+ /* XXX TODO: Only wake if there are pending transmits.. */
+ cw1200_bh_wakeup(priv);
+}
+
+static void cw1200_notify_buffered_tx(struct cw1200_common *priv,
+ struct sk_buff *skb, int link_id, int tid)
+{
+ struct ieee80211_sta *sta;
+ struct ieee80211_hdr *hdr;
+ u8 *buffered;
+ u8 still_buffered = 0;
+
+ if (link_id && tid < CW1200_MAX_TID) {
+ buffered = priv->link_id_db
+ [link_id - 1].buffered;
+
+ spin_lock_bh(&priv->ps_state_lock);
+ if (!WARN_ON(!buffered[tid]))
+ still_buffered = --buffered[tid];
+ spin_unlock_bh(&priv->ps_state_lock);
+
+ if (!still_buffered && tid < CW1200_MAX_TID) {
+ hdr = (struct ieee80211_hdr *)skb->data;
+ rcu_read_lock();
+ sta = ieee80211_find_sta(priv->vif, hdr->addr1);
+ if (sta)
+ ieee80211_sta_set_buffered(sta, tid, false);
+ rcu_read_unlock();
+ }
+ }
+}
+
+void cw1200_skb_dtor(struct cw1200_common *priv,
+ struct sk_buff *skb,
+ const struct cw1200_txpriv *txpriv)
+{
+ skb_pull(skb, txpriv->offset);
+ if (txpriv->rate_id != CW1200_INVALID_RATE_ID) {
+ cw1200_notify_buffered_tx(priv, skb,
+ txpriv->raw_link_id, txpriv->tid);
+ tx_policy_put(priv, txpriv->rate_id);
+ }
+ ieee80211_tx_status(priv->hw, skb);
+}
+
+void cw1200_rx_cb(struct cw1200_common *priv,
+ struct wsm_rx *arg,
+ int link_id,
+ struct sk_buff **skb_p)
+{
+ struct sk_buff *skb = *skb_p;
+ struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+ struct cw1200_link_entry *entry = NULL;
+ unsigned long grace_period;
+
+ bool early_data = false;
+ bool p2p = priv->vif && priv->vif->p2p;
+ size_t hdrlen;
+ hdr->flag = 0;
+
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
+ /* STA is stopped. */
+ goto drop;
+ }
+
+ if (link_id && link_id <= CW1200_MAX_STA_IN_AP_MODE) {
+ entry = &priv->link_id_db[link_id - 1];
+ if (entry->status == CW1200_LINK_SOFT &&
+ ieee80211_is_data(frame->frame_control))
+ early_data = true;
+ entry->timestamp = jiffies;
+ } else if (p2p &&
+ ieee80211_is_action(frame->frame_control) &&
+ (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
+ pr_debug("[RX] Going to MAP&RESET link ID\n");
+ WARN_ON(work_pending(&priv->linkid_reset_work));
+ memcpy(&priv->action_frame_sa[0],
+ ieee80211_get_SA(frame), ETH_ALEN);
+ priv->action_linkid = 0;
+ schedule_work(&priv->linkid_reset_work);
+ }
+
+ if (link_id && p2p &&
+ ieee80211_is_action(frame->frame_control) &&
+ (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
+ /* Link ID already exists for the ACTION frame.
+ * Reset and Remap
+ */
+ WARN_ON(work_pending(&priv->linkid_reset_work));
+ memcpy(&priv->action_frame_sa[0],
+ ieee80211_get_SA(frame), ETH_ALEN);
+ priv->action_linkid = link_id;
+ schedule_work(&priv->linkid_reset_work);
+ }
+ if (arg->status) {
+ if (arg->status == WSM_STATUS_MICFAILURE) {
+ pr_debug("[RX] MIC failure.\n");
+ hdr->flag |= RX_FLAG_MMIC_ERROR;
+ } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) {
+ pr_debug("[RX] No key found.\n");
+ goto drop;
+ } else {
+ pr_debug("[RX] Receive failure: %d.\n",
+ arg->status);
+ goto drop;
+ }
+ }
+
+ if (skb->len < sizeof(struct ieee80211_pspoll)) {
+ wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. Size is lesser than IEEE header.\n");
+ goto drop;
+ }
+
+ if (ieee80211_is_pspoll(frame->frame_control))
+ if (cw1200_handle_pspoll(priv, skb))
+ goto drop;
+
+ hdr->band = ((arg->channel_number & 0xff00) ||
+ (arg->channel_number > 14)) ?
+ IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ;
+ hdr->freq = ieee80211_channel_to_frequency(
+ arg->channel_number,
+ hdr->band);
+
+ if (arg->rx_rate >= 14) {
+ hdr->flag |= RX_FLAG_HT;
+ hdr->rate_idx = arg->rx_rate - 14;
+ } else if (arg->rx_rate >= 4) {
+ hdr->rate_idx = arg->rx_rate - 2;
+ } else {
+ hdr->rate_idx = arg->rx_rate;
+ }
+
+ hdr->signal = (s8)arg->rcpi_rssi;
+ hdr->antenna = 0;
+
+ hdrlen = ieee80211_hdrlen(frame->frame_control);
+
+ if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) {
+ size_t iv_len = 0, icv_len = 0;
+
+ hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED;
+
+ /* Oops... There is no fast way to ask mac80211 about
+ * IV/ICV lengths. Even defineas are not exposed.
+ */
+ switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) {
+ case WSM_RX_STATUS_WEP:
+ iv_len = 4 /* WEP_IV_LEN */;
+ icv_len = 4 /* WEP_ICV_LEN */;
+ break;
+ case WSM_RX_STATUS_TKIP:
+ iv_len = 8 /* TKIP_IV_LEN */;
+ icv_len = 4 /* TKIP_ICV_LEN */
+ + 8 /*MICHAEL_MIC_LEN*/;
+ hdr->flag |= RX_FLAG_MMIC_STRIPPED;
+ break;
+ case WSM_RX_STATUS_AES:
+ iv_len = 8 /* CCMP_HDR_LEN */;
+ icv_len = 8 /* CCMP_MIC_LEN */;
+ break;
+ case WSM_RX_STATUS_WAPI:
+ iv_len = 18 /* WAPI_HDR_LEN */;
+ icv_len = 16 /* WAPI_MIC_LEN */;
+ break;
+ default:
+ pr_warn("Unknown encryption type %d\n",
+ WSM_RX_STATUS_ENCRYPTION(arg->flags));
+ goto drop;
+ }
+
+ /* Firmware strips ICV in case of MIC failure. */
+ if (arg->status == WSM_STATUS_MICFAILURE)
+ icv_len = 0;
+
+ if (skb->len < hdrlen + iv_len + icv_len) {
+ wiphy_warn(priv->hw->wiphy, "Malformed SDU rx'ed. Size is lesser than crypto headers.\n");
+ goto drop;
+ }
+
+ /* Remove IV, ICV and MIC */
+ skb_trim(skb, skb->len - icv_len);
+ memmove(skb->data + iv_len, skb->data, hdrlen);
+ skb_pull(skb, iv_len);
+ }
+
+ /* Remove TSF from the end of frame */
+ if (arg->flags & WSM_RX_STATUS_TSF_INCLUDED) {
+ memcpy(&hdr->mactime, skb->data + skb->len - 8, 8);
+ hdr->mactime = le64_to_cpu(hdr->mactime);
+ if (skb->len >= 8)
+ skb_trim(skb, skb->len - 8);
+ } else {
+ hdr->mactime = 0;
+ }
+
+ cw1200_debug_rxed(priv);
+ if (arg->flags & WSM_RX_STATUS_AGGREGATE)
+ cw1200_debug_rxed_agg(priv);
+
+ if (ieee80211_is_action(frame->frame_control) &&
+ (arg->flags & WSM_RX_STATUS_ADDRESS1)) {
+ if (cw1200_handle_action_rx(priv, skb))
+ return;
+ } else if (ieee80211_is_beacon(frame->frame_control) &&
+ !arg->status &&
+ !memcmp(ieee80211_get_SA(frame), priv->vif->bss_conf.bssid,
+ ETH_ALEN)) {
+ const u8 *tim_ie;
+ u8 *ies = ((struct ieee80211_mgmt *)
+ (skb->data))->u.beacon.variable;
+ size_t ies_len = skb->len - (ies - (u8 *)(skb->data));
+
+ tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies, ies_len);
+ if (tim_ie) {
+ struct ieee80211_tim_ie *tim =
+ (struct ieee80211_tim_ie *)&tim_ie[2];
+
+ if (priv->join_dtim_period != tim->dtim_period) {
+ priv->join_dtim_period = tim->dtim_period;
+ queue_work(priv->workqueue,
+ &priv->set_beacon_wakeup_period_work);
+ }
+ }
+
+ /* Disable beacon filter once we're associated... */
+ if (priv->disable_beacon_filter &&
+ (priv->vif->bss_conf.assoc ||
+ priv->vif->bss_conf.ibss_joined)) {
+ priv->disable_beacon_filter = false;
+ queue_work(priv->workqueue,
+ &priv->update_filtering_work);
+ }
+ }
+
+ /* Stay awake after frame is received to give
+ * userspace chance to react and acquire appropriate
+ * wakelock.
+ */
+ if (ieee80211_is_auth(frame->frame_control))
+ grace_period = 5 * HZ;
+ else if (ieee80211_is_deauth(frame->frame_control))
+ grace_period = 5 * HZ;
+ else
+ grace_period = 1 * HZ;
+ cw1200_pm_stay_awake(&priv->pm_state, grace_period);
+
+ if (early_data) {
+ spin_lock_bh(&priv->ps_state_lock);
+ /* Double-check status with lock held */
+ if (entry->status == CW1200_LINK_SOFT)
+ skb_queue_tail(&entry->rx_queue, skb);
+ else
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ spin_unlock_bh(&priv->ps_state_lock);
+ } else {
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ }
+ *skb_p = NULL;
+
+ return;
+
+drop:
+ /* TODO: update failure counters */
+ return;
+}
+
+/* ******************************************************************** */
+/* Security */
+
+int cw1200_alloc_key(struct cw1200_common *priv)
+{
+ int idx;
+
+ idx = ffs(~priv->key_map) - 1;
+ if (idx < 0 || idx > WSM_KEY_MAX_INDEX)
+ return -1;
+
+ priv->key_map |= BIT(idx);
+ priv->keys[idx].index = idx;
+ return idx;
+}
+
+void cw1200_free_key(struct cw1200_common *priv, int idx)
+{
+ BUG_ON(!(priv->key_map & BIT(idx)));
+ memset(&priv->keys[idx], 0, sizeof(priv->keys[idx]));
+ priv->key_map &= ~BIT(idx);
+}
+
+void cw1200_free_keys(struct cw1200_common *priv)
+{
+ memset(&priv->keys, 0, sizeof(priv->keys));
+ priv->key_map = 0;
+}
+
+int cw1200_upload_keys(struct cw1200_common *priv)
+{
+ int idx, ret = 0;
+ for (idx = 0; idx <= WSM_KEY_MAX_INDEX; ++idx)
+ if (priv->key_map & BIT(idx)) {
+ ret = wsm_add_key(priv, &priv->keys[idx]);
+ if (ret < 0)
+ break;
+ }
+ return ret;
+}
+
+/* Workaround for WFD test case 6.1.10 */
+void cw1200_link_id_reset(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, linkid_reset_work);
+ int temp_linkid;
+
+ if (!priv->action_linkid) {
+ /* In GO mode we can receive ACTION frames without a linkID */
+ temp_linkid = cw1200_alloc_link_id(priv,
+ &priv->action_frame_sa[0]);
+ WARN_ON(!temp_linkid);
+ if (temp_linkid) {
+ /* Make sure we execute the WQ */
+ flush_workqueue(priv->workqueue);
+ /* Release the link ID */
+ spin_lock_bh(&priv->ps_state_lock);
+ priv->link_id_db[temp_linkid - 1].prev_status =
+ priv->link_id_db[temp_linkid - 1].status;
+ priv->link_id_db[temp_linkid - 1].status =
+ CW1200_LINK_RESET;
+ spin_unlock_bh(&priv->ps_state_lock);
+ wsm_lock_tx_async(priv);
+ if (queue_work(priv->workqueue,
+ &priv->link_id_work) <= 0)
+ wsm_unlock_tx(priv);
+ }
+ } else {
+ spin_lock_bh(&priv->ps_state_lock);
+ priv->link_id_db[priv->action_linkid - 1].prev_status =
+ priv->link_id_db[priv->action_linkid - 1].status;
+ priv->link_id_db[priv->action_linkid - 1].status =
+ CW1200_LINK_RESET_REMAP;
+ spin_unlock_bh(&priv->ps_state_lock);
+ wsm_lock_tx_async(priv);
+ if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
+ wsm_unlock_tx(priv);
+ flush_workqueue(priv->workqueue);
+ }
+}
+
+int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac)
+{
+ int i, ret = 0;
+ spin_lock_bh(&priv->ps_state_lock);
+ for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
+ if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) &&
+ priv->link_id_db[i].status) {
+ priv->link_id_db[i].timestamp = jiffies;
+ ret = i + 1;
+ break;
+ }
+ }
+ spin_unlock_bh(&priv->ps_state_lock);
+ return ret;
+}
+
+int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac)
+{
+ int i, ret = 0;
+ unsigned long max_inactivity = 0;
+ unsigned long now = jiffies;
+
+ spin_lock_bh(&priv->ps_state_lock);
+ for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
+ if (!priv->link_id_db[i].status) {
+ ret = i + 1;
+ break;
+ } else if (priv->link_id_db[i].status != CW1200_LINK_HARD &&
+ !priv->tx_queue_stats.link_map_cache[i + 1]) {
+ unsigned long inactivity =
+ now - priv->link_id_db[i].timestamp;
+ if (inactivity < max_inactivity)
+ continue;
+ max_inactivity = inactivity;
+ ret = i + 1;
+ }
+ }
+ if (ret) {
+ struct cw1200_link_entry *entry = &priv->link_id_db[ret - 1];
+ pr_debug("[AP] STA added, link_id: %d\n", ret);
+ entry->status = CW1200_LINK_RESERVE;
+ memcpy(&entry->mac, mac, ETH_ALEN);
+ memset(&entry->buffered, 0, CW1200_MAX_TID);
+ skb_queue_head_init(&entry->rx_queue);
+ wsm_lock_tx_async(priv);
+ if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
+ wsm_unlock_tx(priv);
+ } else {
+ wiphy_info(priv->hw->wiphy,
+ "[AP] Early: no more link IDs available.\n");
+ }
+
+ spin_unlock_bh(&priv->ps_state_lock);
+ return ret;
+}
+
+void cw1200_link_id_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, link_id_work);
+ wsm_flush_tx(priv);
+ cw1200_link_id_gc_work(&priv->link_id_gc_work.work);
+ wsm_unlock_tx(priv);
+}
+
+void cw1200_link_id_gc_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, link_id_gc_work.work);
+ struct wsm_reset reset = {
+ .reset_statistics = false,
+ };
+ struct wsm_map_link map_link = {
+ .link_id = 0,
+ };
+ unsigned long now = jiffies;
+ unsigned long next_gc = -1;
+ long ttl;
+ bool need_reset;
+ u32 mask;
+ int i;
+
+ if (priv->join_status != CW1200_JOIN_STATUS_AP)
+ return;
+
+ wsm_lock_tx(priv);
+ spin_lock_bh(&priv->ps_state_lock);
+ for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
+ need_reset = false;
+ mask = BIT(i + 1);
+ if (priv->link_id_db[i].status == CW1200_LINK_RESERVE ||
+ (priv->link_id_db[i].status == CW1200_LINK_HARD &&
+ !(priv->link_id_map & mask))) {
+ if (priv->link_id_map & mask) {
+ priv->sta_asleep_mask &= ~mask;
+ priv->pspoll_mask &= ~mask;
+ need_reset = true;
+ }
+ priv->link_id_map |= mask;
+ if (priv->link_id_db[i].status != CW1200_LINK_HARD)
+ priv->link_id_db[i].status = CW1200_LINK_SOFT;
+ memcpy(map_link.mac_addr, priv->link_id_db[i].mac,
+ ETH_ALEN);
+ spin_unlock_bh(&priv->ps_state_lock);
+ if (need_reset) {
+ reset.link_id = i + 1;
+ wsm_reset(priv, &reset);
+ }
+ map_link.link_id = i + 1;
+ wsm_map_link(priv, &map_link);
+ next_gc = min(next_gc, CW1200_LINK_ID_GC_TIMEOUT);
+ spin_lock_bh(&priv->ps_state_lock);
+ } else if (priv->link_id_db[i].status == CW1200_LINK_SOFT) {
+ ttl = priv->link_id_db[i].timestamp - now +
+ CW1200_LINK_ID_GC_TIMEOUT;
+ if (ttl <= 0) {
+ need_reset = true;
+ priv->link_id_db[i].status = CW1200_LINK_OFF;
+ priv->link_id_map &= ~mask;
+ priv->sta_asleep_mask &= ~mask;
+ priv->pspoll_mask &= ~mask;
+ memset(map_link.mac_addr, 0, ETH_ALEN);
+ spin_unlock_bh(&priv->ps_state_lock);
+ reset.link_id = i + 1;
+ wsm_reset(priv, &reset);
+ spin_lock_bh(&priv->ps_state_lock);
+ } else {
+ next_gc = min_t(unsigned long, next_gc, ttl);
+ }
+ } else if (priv->link_id_db[i].status == CW1200_LINK_RESET ||
+ priv->link_id_db[i].status ==
+ CW1200_LINK_RESET_REMAP) {
+ int status = priv->link_id_db[i].status;
+ priv->link_id_db[i].status =
+ priv->link_id_db[i].prev_status;
+ priv->link_id_db[i].timestamp = now;
+ reset.link_id = i + 1;
+ spin_unlock_bh(&priv->ps_state_lock);
+ wsm_reset(priv, &reset);
+ if (status == CW1200_LINK_RESET_REMAP) {
+ memcpy(map_link.mac_addr,
+ priv->link_id_db[i].mac,
+ ETH_ALEN);
+ map_link.link_id = i + 1;
+ wsm_map_link(priv, &map_link);
+ next_gc = min(next_gc,
+ CW1200_LINK_ID_GC_TIMEOUT);
+ }
+ spin_lock_bh(&priv->ps_state_lock);
+ }
+ if (need_reset) {
+ skb_queue_purge(&priv->link_id_db[i].rx_queue);
+ pr_debug("[AP] STA removed, link_id: %d\n",
+ reset.link_id);
+ }
+ }
+ spin_unlock_bh(&priv->ps_state_lock);
+ if (next_gc != -1)
+ queue_delayed_work(priv->workqueue,
+ &priv->link_id_gc_work, next_gc);
+ wsm_unlock_tx(priv);
+}
diff --git a/drivers/net/wireless/cw1200/txrx.h b/drivers/net/wireless/cw1200/txrx.h
new file mode 100644
index 000000000000..492a4e14213b
--- /dev/null
+++ b/drivers/net/wireless/cw1200/txrx.h
@@ -0,0 +1,106 @@
+/*
+ * Datapath interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 CW1200_TXRX_H
+#define CW1200_TXRX_H
+
+#include <linux/list.h>
+
+/* extern */ struct ieee80211_hw;
+/* extern */ struct sk_buff;
+/* extern */ struct wsm_tx;
+/* extern */ struct wsm_rx;
+/* extern */ struct wsm_tx_confirm;
+/* extern */ struct cw1200_txpriv;
+
+struct tx_policy {
+ union {
+ __le32 tbl[3];
+ u8 raw[12];
+ };
+ u8 defined;
+ u8 usage_count;
+ u8 retry_count;
+ u8 uploaded;
+};
+
+struct tx_policy_cache_entry {
+ struct tx_policy policy;
+ struct list_head link;
+};
+
+#define TX_POLICY_CACHE_SIZE (8)
+struct tx_policy_cache {
+ struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE];
+ struct list_head used;
+ struct list_head free;
+ spinlock_t lock; /* Protect policy cache */
+};
+
+/* ******************************************************************** */
+/* TX policy cache */
+/* Intention of TX policy cache is an overcomplicated WSM API.
+ * Device does not accept per-PDU tx retry sequence.
+ * It uses "tx retry policy id" instead, so driver code has to sync
+ * linux tx retry sequences with a retry policy table in the device.
+ */
+void tx_policy_init(struct cw1200_common *priv);
+void tx_policy_upload_work(struct work_struct *work);
+void tx_policy_clean(struct cw1200_common *priv);
+
+/* ******************************************************************** */
+/* TX implementation */
+
+u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv,
+ u32 rates);
+void cw1200_tx(struct ieee80211_hw *dev,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb);
+void cw1200_skb_dtor(struct cw1200_common *priv,
+ struct sk_buff *skb,
+ const struct cw1200_txpriv *txpriv);
+
+/* ******************************************************************** */
+/* WSM callbacks */
+
+void cw1200_tx_confirm_cb(struct cw1200_common *priv,
+ int link_id,
+ struct wsm_tx_confirm *arg);
+void cw1200_rx_cb(struct cw1200_common *priv,
+ struct wsm_rx *arg,
+ int link_id,
+ struct sk_buff **skb_p);
+
+/* ******************************************************************** */
+/* Timeout */
+
+void cw1200_tx_timeout(struct work_struct *work);
+
+/* ******************************************************************** */
+/* Security */
+int cw1200_alloc_key(struct cw1200_common *priv);
+void cw1200_free_key(struct cw1200_common *priv, int idx);
+void cw1200_free_keys(struct cw1200_common *priv);
+int cw1200_upload_keys(struct cw1200_common *priv);
+
+/* ******************************************************************** */
+/* Workaround for WFD test case 6.1.10 */
+void cw1200_link_id_reset(struct work_struct *work);
+
+#define CW1200_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ))
+
+int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac);
+int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac);
+void cw1200_link_id_work(struct work_struct *work);
+void cw1200_link_id_gc_work(struct work_struct *work);
+
+
+#endif /* CW1200_TXRX_H */
diff --git a/drivers/net/wireless/cw1200/wsm.c b/drivers/net/wireless/cw1200/wsm.c
new file mode 100644
index 000000000000..d95094fdcc50
--- /dev/null
+++ b/drivers/net/wireless/cw1200/wsm.c
@@ -0,0 +1,1823 @@
+/*
+ * WSM host interface (HI) implementation for
+ * ST-Ericsson CW1200 mac80211 drivers.
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/skbuff.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/random.h>
+
+#include "cw1200.h"
+#include "wsm.h"
+#include "bh.h"
+#include "sta.h"
+#include "debug.h"
+
+#define WSM_CMD_TIMEOUT (2 * HZ) /* With respect to interrupt loss */
+#define WSM_CMD_START_TIMEOUT (7 * HZ)
+#define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */
+#define WSM_CMD_MAX_TIMEOUT (3 * HZ)
+
+#define WSM_SKIP(buf, size) \
+ do { \
+ if ((buf)->data + size > (buf)->end) \
+ goto underflow; \
+ (buf)->data += size; \
+ } while (0)
+
+#define WSM_GET(buf, ptr, size) \
+ do { \
+ if ((buf)->data + size > (buf)->end) \
+ goto underflow; \
+ memcpy(ptr, (buf)->data, size); \
+ (buf)->data += size; \
+ } while (0)
+
+#define __WSM_GET(buf, type, cvt) \
+ ({ \
+ type val; \
+ if ((buf)->data + sizeof(type) > (buf)->end) \
+ goto underflow; \
+ val = cvt(*(type *)(buf)->data); \
+ (buf)->data += sizeof(type); \
+ val; \
+ })
+
+#define WSM_GET8(buf) __WSM_GET(buf, u8, (u8))
+#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16_to_cpu)
+#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32_to_cpu)
+
+#define WSM_PUT(buf, ptr, size) \
+ do { \
+ if ((buf)->data + size > (buf)->end) \
+ if (wsm_buf_reserve((buf), size)) \
+ goto nomem; \
+ memcpy((buf)->data, ptr, size); \
+ (buf)->data += size; \
+ } while (0)
+
+#define __WSM_PUT(buf, val, type, cvt) \
+ do { \
+ if ((buf)->data + sizeof(type) > (buf)->end) \
+ if (wsm_buf_reserve((buf), sizeof(type))) \
+ goto nomem; \
+ *(type *)(buf)->data = cvt(val); \
+ (buf)->data += sizeof(type); \
+ } while (0)
+
+#define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, (u8))
+#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __cpu_to_le16)
+#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __cpu_to_le32)
+
+static void wsm_buf_reset(struct wsm_buf *buf);
+static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size);
+
+static int wsm_cmd_send(struct cw1200_common *priv,
+ struct wsm_buf *buf,
+ void *arg, u16 cmd, long tmo);
+
+#define wsm_cmd_lock(__priv) mutex_lock(&((__priv)->wsm_cmd_mux))
+#define wsm_cmd_unlock(__priv) mutex_unlock(&((__priv)->wsm_cmd_mux))
+
+/* ******************************************************************** */
+/* WSM API implementation */
+
+static int wsm_generic_confirm(struct cw1200_common *priv,
+ void *arg,
+ struct wsm_buf *buf)
+{
+ u32 status = WSM_GET32(buf);
+ if (status != WSM_STATUS_SUCCESS)
+ return -EINVAL;
+ return 0;
+
+underflow:
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+int wsm_configuration(struct cw1200_common *priv, struct wsm_configuration *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime);
+ WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime);
+ WSM_PUT32(buf, arg->dot11RtsThreshold);
+
+ /* DPD block. */
+ WSM_PUT16(buf, arg->dpdData_size + 12);
+ WSM_PUT16(buf, 1); /* DPD version */
+ WSM_PUT(buf, arg->dot11StationId, ETH_ALEN);
+ WSM_PUT16(buf, 5); /* DPD flags */
+ WSM_PUT(buf, arg->dpdData, arg->dpdData_size);
+
+ ret = wsm_cmd_send(priv, buf, arg,
+ WSM_CONFIGURATION_REQ_ID, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+static int wsm_configuration_confirm(struct cw1200_common *priv,
+ struct wsm_configuration *arg,
+ struct wsm_buf *buf)
+{
+ int i;
+ int status;
+
+ status = WSM_GET32(buf);
+ if (WARN_ON(status != WSM_STATUS_SUCCESS))
+ return -EINVAL;
+
+ WSM_GET(buf, arg->dot11StationId, ETH_ALEN);
+ arg->dot11FrequencyBandsSupported = WSM_GET8(buf);
+ WSM_SKIP(buf, 1);
+ arg->supportedRateMask = WSM_GET32(buf);
+ for (i = 0; i < 2; ++i) {
+ arg->txPowerRange[i].min_power_level = WSM_GET32(buf);
+ arg->txPowerRange[i].max_power_level = WSM_GET32(buf);
+ arg->txPowerRange[i].stepping = WSM_GET32(buf);
+ }
+ return 0;
+
+underflow:
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ u16 cmd = WSM_RESET_REQ_ID | WSM_TX_LINK_ID(arg->link_id);
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT32(buf, arg->reset_statistics ? 0 : 1);
+ ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT);
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+struct wsm_mib {
+ u16 mib_id;
+ void *buf;
+ size_t buf_size;
+};
+
+int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *_buf,
+ size_t buf_size)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ struct wsm_mib mib_buf = {
+ .mib_id = mib_id,
+ .buf = _buf,
+ .buf_size = buf_size,
+ };
+ wsm_cmd_lock(priv);
+
+ WSM_PUT16(buf, mib_id);
+ WSM_PUT16(buf, 0);
+
+ ret = wsm_cmd_send(priv, buf, &mib_buf,
+ WSM_READ_MIB_REQ_ID, WSM_CMD_TIMEOUT);
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+static int wsm_read_mib_confirm(struct cw1200_common *priv,
+ struct wsm_mib *arg,
+ struct wsm_buf *buf)
+{
+ u16 size;
+ if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS))
+ return -EINVAL;
+
+ if (WARN_ON(WSM_GET16(buf) != arg->mib_id))
+ return -EINVAL;
+
+ size = WSM_GET16(buf);
+ if (size > arg->buf_size)
+ size = arg->buf_size;
+
+ WSM_GET(buf, arg->buf, size);
+ arg->buf_size = size;
+ return 0;
+
+underflow:
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *_buf,
+ size_t buf_size)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ struct wsm_mib mib_buf = {
+ .mib_id = mib_id,
+ .buf = _buf,
+ .buf_size = buf_size,
+ };
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT16(buf, mib_id);
+ WSM_PUT16(buf, buf_size);
+ WSM_PUT(buf, _buf, buf_size);
+
+ ret = wsm_cmd_send(priv, buf, &mib_buf,
+ WSM_WRITE_MIB_REQ_ID, WSM_CMD_TIMEOUT);
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+static int wsm_write_mib_confirm(struct cw1200_common *priv,
+ struct wsm_mib *arg,
+ struct wsm_buf *buf)
+{
+ int ret;
+
+ ret = wsm_generic_confirm(priv, arg, buf);
+ if (ret)
+ return ret;
+
+ if (arg->mib_id == WSM_MIB_ID_OPERATIONAL_POWER_MODE) {
+ /* OperationalMode: update PM status. */
+ const char *p = arg->buf;
+ cw1200_enable_powersave(priv, (p[0] & 0x0F) ? true : false);
+ }
+ return 0;
+}
+
+/* ******************************************************************** */
+
+int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg)
+{
+ int i;
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ if (arg->num_channels > 48)
+ return -EINVAL;
+
+ if (arg->num_ssids > 2)
+ return -EINVAL;
+
+ if (arg->band > 1)
+ return -EINVAL;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT8(buf, arg->band);
+ WSM_PUT8(buf, arg->type);
+ WSM_PUT8(buf, arg->flags);
+ WSM_PUT8(buf, arg->max_tx_rate);
+ WSM_PUT32(buf, arg->auto_scan_interval);
+ WSM_PUT8(buf, arg->num_probes);
+ WSM_PUT8(buf, arg->num_channels);
+ WSM_PUT8(buf, arg->num_ssids);
+ WSM_PUT8(buf, arg->probe_delay);
+
+ for (i = 0; i < arg->num_channels; ++i) {
+ WSM_PUT16(buf, arg->ch[i].number);
+ WSM_PUT16(buf, 0);
+ WSM_PUT32(buf, arg->ch[i].min_chan_time);
+ WSM_PUT32(buf, arg->ch[i].max_chan_time);
+ WSM_PUT32(buf, 0);
+ }
+
+ for (i = 0; i < arg->num_ssids; ++i) {
+ WSM_PUT32(buf, arg->ssids[i].length);
+ WSM_PUT(buf, &arg->ssids[i].ssid[0],
+ sizeof(arg->ssids[i].ssid));
+ }
+
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_START_SCAN_REQ_ID, WSM_CMD_TIMEOUT);
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_stop_scan(struct cw1200_common *priv)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ wsm_cmd_lock(priv);
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_STOP_SCAN_REQ_ID, WSM_CMD_TIMEOUT);
+ wsm_cmd_unlock(priv);
+ return ret;
+}
+
+
+static int wsm_tx_confirm(struct cw1200_common *priv,
+ struct wsm_buf *buf,
+ int link_id)
+{
+ struct wsm_tx_confirm tx_confirm;
+
+ tx_confirm.packet_id = WSM_GET32(buf);
+ tx_confirm.status = WSM_GET32(buf);
+ tx_confirm.tx_rate = WSM_GET8(buf);
+ tx_confirm.ack_failures = WSM_GET8(buf);
+ tx_confirm.flags = WSM_GET16(buf);
+ tx_confirm.media_delay = WSM_GET32(buf);
+ tx_confirm.tx_queue_delay = WSM_GET32(buf);
+
+ cw1200_tx_confirm_cb(priv, link_id, &tx_confirm);
+ return 0;
+
+underflow:
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+static int wsm_multi_tx_confirm(struct cw1200_common *priv,
+ struct wsm_buf *buf, int link_id)
+{
+ int ret;
+ int count;
+ int i;
+
+ count = WSM_GET32(buf);
+ if (WARN_ON(count <= 0))
+ return -EINVAL;
+
+ if (count > 1) {
+ /* We already released one buffer, now for the rest */
+ ret = wsm_release_tx_buffer(priv, count - 1);
+ if (ret < 0)
+ return ret;
+ else if (ret > 0)
+ cw1200_bh_wakeup(priv);
+ }
+
+ cw1200_debug_txed_multi(priv, count);
+ for (i = 0; i < count; ++i) {
+ ret = wsm_tx_confirm(priv, buf, link_id);
+ if (ret)
+ return ret;
+ }
+ return ret;
+
+underflow:
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+static int wsm_join_confirm(struct cw1200_common *priv,
+ struct wsm_join_cnf *arg,
+ struct wsm_buf *buf)
+{
+ arg->status = WSM_GET32(buf);
+ if (WARN_ON(arg->status) != WSM_STATUS_SUCCESS)
+ return -EINVAL;
+
+ arg->min_power_level = WSM_GET32(buf);
+ arg->max_power_level = WSM_GET32(buf);
+
+ return 0;
+
+underflow:
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+int wsm_join(struct cw1200_common *priv, struct wsm_join *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ struct wsm_join_cnf resp;
+ wsm_cmd_lock(priv);
+
+ WSM_PUT8(buf, arg->mode);
+ WSM_PUT8(buf, arg->band);
+ WSM_PUT16(buf, arg->channel_number);
+ WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid));
+ WSM_PUT16(buf, arg->atim_window);
+ WSM_PUT8(buf, arg->preamble_type);
+ WSM_PUT8(buf, arg->probe_for_join);
+ WSM_PUT8(buf, arg->dtim_period);
+ WSM_PUT8(buf, arg->flags);
+ WSM_PUT32(buf, arg->ssid_len);
+ WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid));
+ WSM_PUT32(buf, arg->beacon_interval);
+ WSM_PUT32(buf, arg->basic_rate_set);
+
+ priv->tx_burst_idx = -1;
+ ret = wsm_cmd_send(priv, buf, &resp,
+ WSM_JOIN_REQ_ID, WSM_CMD_TIMEOUT);
+ /* TODO: Update state based on resp.min|max_power_level */
+
+ priv->join_complete_status = resp.status;
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_bss_params(struct cw1200_common *priv,
+ const struct wsm_set_bss_params *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT8(buf, (arg->reset_beacon_loss ? 0x1 : 0));
+ WSM_PUT8(buf, arg->beacon_lost_count);
+ WSM_PUT16(buf, arg->aid);
+ WSM_PUT32(buf, arg->operational_rate_set);
+
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_SET_BSS_PARAMS_REQ_ID, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT(buf, arg, sizeof(*arg));
+
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_ADD_KEY_REQ_ID, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_remove_key(struct cw1200_common *priv, const struct wsm_remove_key *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT8(buf, arg->index);
+ WSM_PUT8(buf, 0);
+ WSM_PUT16(buf, 0);
+
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_REMOVE_KEY_REQ_ID, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_tx_queue_params(struct cw1200_common *priv,
+ const struct wsm_set_tx_queue_params *arg, u8 id)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1};
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT8(buf, queue_id_to_wmm_aci[id]);
+ WSM_PUT8(buf, 0);
+ WSM_PUT8(buf, arg->ackPolicy);
+ WSM_PUT8(buf, 0);
+ WSM_PUT32(buf, arg->maxTransmitLifetime);
+ WSM_PUT16(buf, arg->allowedMediumTime);
+ WSM_PUT16(buf, 0);
+
+ ret = wsm_cmd_send(priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_edca_params(struct cw1200_common *priv,
+ const struct wsm_edca_params *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ /* Implemented according to specification. */
+
+ WSM_PUT16(buf, arg->params[3].cwmin);
+ WSM_PUT16(buf, arg->params[2].cwmin);
+ WSM_PUT16(buf, arg->params[1].cwmin);
+ WSM_PUT16(buf, arg->params[0].cwmin);
+
+ WSM_PUT16(buf, arg->params[3].cwmax);
+ WSM_PUT16(buf, arg->params[2].cwmax);
+ WSM_PUT16(buf, arg->params[1].cwmax);
+ WSM_PUT16(buf, arg->params[0].cwmax);
+
+ WSM_PUT8(buf, arg->params[3].aifns);
+ WSM_PUT8(buf, arg->params[2].aifns);
+ WSM_PUT8(buf, arg->params[1].aifns);
+ WSM_PUT8(buf, arg->params[0].aifns);
+
+ WSM_PUT16(buf, arg->params[3].txop_limit);
+ WSM_PUT16(buf, arg->params[2].txop_limit);
+ WSM_PUT16(buf, arg->params[1].txop_limit);
+ WSM_PUT16(buf, arg->params[0].txop_limit);
+
+ WSM_PUT32(buf, arg->params[3].max_rx_lifetime);
+ WSM_PUT32(buf, arg->params[2].max_rx_lifetime);
+ WSM_PUT32(buf, arg->params[1].max_rx_lifetime);
+ WSM_PUT32(buf, arg->params[0].max_rx_lifetime);
+
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_EDCA_PARAMS_REQ_ID, WSM_CMD_TIMEOUT);
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_switch_channel(struct cw1200_common *priv,
+ const struct wsm_switch_channel *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT8(buf, arg->mode);
+ WSM_PUT8(buf, arg->switch_count);
+ WSM_PUT16(buf, arg->channel_number);
+
+ priv->channel_switch_in_progress = 1;
+
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_SWITCH_CHANNEL_REQ_ID, WSM_CMD_TIMEOUT);
+ if (ret)
+ priv->channel_switch_in_progress = 0;
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ priv->ps_mode_switch_in_progress = 1;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT8(buf, arg->mode);
+ WSM_PUT8(buf, arg->fast_psm_idle_period);
+ WSM_PUT8(buf, arg->ap_psm_change_period);
+ WSM_PUT8(buf, arg->min_auto_pspoll_period);
+
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_SET_PM_REQ_ID, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT8(buf, arg->mode);
+ WSM_PUT8(buf, arg->band);
+ WSM_PUT16(buf, arg->channel_number);
+ WSM_PUT32(buf, arg->ct_window);
+ WSM_PUT32(buf, arg->beacon_interval);
+ WSM_PUT8(buf, arg->dtim_period);
+ WSM_PUT8(buf, arg->preamble);
+ WSM_PUT8(buf, arg->probe_delay);
+ WSM_PUT8(buf, arg->ssid_len);
+ WSM_PUT(buf, arg->ssid, sizeof(arg->ssid));
+ WSM_PUT32(buf, arg->basic_rate_set);
+
+ priv->tx_burst_idx = -1;
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_START_REQ_ID, WSM_CMD_START_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_beacon_transmit(struct cw1200_common *priv,
+ const struct wsm_beacon_transmit *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT32(buf, arg->enable_beaconing ? 1 : 0);
+
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_BEACON_TRANSMIT_REQ_ID, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_start_find(struct cw1200_common *priv)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+ ret = wsm_cmd_send(priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT);
+ wsm_cmd_unlock(priv);
+ return ret;
+}
+
+/* ******************************************************************** */
+
+int wsm_stop_find(struct cw1200_common *priv)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+ ret = wsm_cmd_send(priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT);
+ wsm_cmd_unlock(priv);
+ return ret;
+}
+
+/* ******************************************************************** */
+
+int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ u16 cmd = 0x001C | WSM_TX_LINK_ID(arg->link_id);
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr));
+ WSM_PUT16(buf, 0);
+
+ ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_update_ie(struct cw1200_common *priv,
+ const struct wsm_update_ie *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT16(buf, arg->what);
+ WSM_PUT16(buf, arg->count);
+ WSM_PUT(buf, arg->ies, arg->length);
+
+ ret = wsm_cmd_send(priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+int wsm_set_probe_responder(struct cw1200_common *priv, bool enable)
+{
+ priv->rx_filter.probeResponder = enable;
+ return wsm_set_rx_filter(priv, &priv->rx_filter);
+}
+
+/* ******************************************************************** */
+/* WSM indication events implementation */
+const char * const cw1200_fw_types[] = {
+ "ETF",
+ "WFM",
+ "WSM",
+ "HI test",
+ "Platform test"
+};
+
+static int wsm_startup_indication(struct cw1200_common *priv,
+ struct wsm_buf *buf)
+{
+ priv->wsm_caps.input_buffers = WSM_GET16(buf);
+ priv->wsm_caps.input_buffer_size = WSM_GET16(buf);
+ priv->wsm_caps.hw_id = WSM_GET16(buf);
+ priv->wsm_caps.hw_subid = WSM_GET16(buf);
+ priv->wsm_caps.status = WSM_GET16(buf);
+ priv->wsm_caps.fw_cap = WSM_GET16(buf);
+ priv->wsm_caps.fw_type = WSM_GET16(buf);
+ priv->wsm_caps.fw_api = WSM_GET16(buf);
+ priv->wsm_caps.fw_build = WSM_GET16(buf);
+ priv->wsm_caps.fw_ver = WSM_GET16(buf);
+ WSM_GET(buf, priv->wsm_caps.fw_label, sizeof(priv->wsm_caps.fw_label));
+ priv->wsm_caps.fw_label[sizeof(priv->wsm_caps.fw_label) - 1] = 0; /* Do not trust FW too much... */
+
+ if (WARN_ON(priv->wsm_caps.status))
+ return -EINVAL;
+
+ if (WARN_ON(priv->wsm_caps.fw_type > 4))
+ return -EINVAL;
+
+ pr_info("CW1200 WSM init done.\n"
+ " Input buffers: %d x %d bytes\n"
+ " Hardware: %d.%d\n"
+ " %s firmware [%s], ver: %d, build: %d,"
+ " api: %d, cap: 0x%.4X\n",
+ priv->wsm_caps.input_buffers,
+ priv->wsm_caps.input_buffer_size,
+ priv->wsm_caps.hw_id, priv->wsm_caps.hw_subid,
+ cw1200_fw_types[priv->wsm_caps.fw_type],
+ priv->wsm_caps.fw_label, priv->wsm_caps.fw_ver,
+ priv->wsm_caps.fw_build,
+ priv->wsm_caps.fw_api, priv->wsm_caps.fw_cap);
+
+ /* Disable unsupported frequency bands */
+ if (!(priv->wsm_caps.fw_cap & 0x1))
+ priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
+ if (!(priv->wsm_caps.fw_cap & 0x2))
+ priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
+
+ priv->firmware_ready = 1;
+ wake_up(&priv->wsm_startup_done);
+ return 0;
+
+underflow:
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+static int wsm_receive_indication(struct cw1200_common *priv,
+ int link_id,
+ struct wsm_buf *buf,
+ struct sk_buff **skb_p)
+{
+ struct wsm_rx rx;
+ struct ieee80211_hdr *hdr;
+ size_t hdr_len;
+ __le16 fctl;
+
+ rx.status = WSM_GET32(buf);
+ rx.channel_number = WSM_GET16(buf);
+ rx.rx_rate = WSM_GET8(buf);
+ rx.rcpi_rssi = WSM_GET8(buf);
+ rx.flags = WSM_GET32(buf);
+
+ /* FW Workaround: Drop probe resp or
+ beacon when RSSI is 0
+ */
+ hdr = (struct ieee80211_hdr *)(*skb_p)->data;
+
+ if (!rx.rcpi_rssi &&
+ (ieee80211_is_probe_resp(hdr->frame_control) ||
+ ieee80211_is_beacon(hdr->frame_control)))
+ return 0;
+
+ /* If no RSSI subscription has been made,
+ * convert RCPI to RSSI here
+ */
+ if (!priv->cqm_use_rssi)
+ rx.rcpi_rssi = rx.rcpi_rssi / 2 - 110;
+
+ fctl = *(__le16 *)buf->data;
+ hdr_len = buf->data - buf->begin;
+ skb_pull(*skb_p, hdr_len);
+ if (!rx.status && ieee80211_is_deauth(fctl)) {
+ if (priv->join_status == CW1200_JOIN_STATUS_STA) {
+ /* Shedule unjoin work */
+ pr_debug("[WSM] Issue unjoin command (RX).\n");
+ wsm_lock_tx_async(priv);
+ if (queue_work(priv->workqueue,
+ &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+ }
+ }
+ cw1200_rx_cb(priv, &rx, link_id, skb_p);
+ if (*skb_p)
+ skb_push(*skb_p, hdr_len);
+
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+
+static int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf)
+{
+ int first;
+ struct cw1200_wsm_event *event;
+
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
+ /* STA is stopped. */
+ return 0;
+ }
+
+ event = kzalloc(sizeof(struct cw1200_wsm_event), GFP_KERNEL);
+ if (!event)
+ return -ENOMEM;
+
+ event->evt.id = __le32_to_cpu(WSM_GET32(buf));
+ event->evt.data = __le32_to_cpu(WSM_GET32(buf));
+
+ pr_debug("[WSM] Event: %d(%d)\n",
+ event->evt.id, event->evt.data);
+
+ spin_lock(&priv->event_queue_lock);
+ first = list_empty(&priv->event_queue);
+ list_add_tail(&event->link, &priv->event_queue);
+ spin_unlock(&priv->event_queue_lock);
+
+ if (first)
+ queue_work(priv->workqueue, &priv->event_handler);
+
+ return 0;
+
+underflow:
+ kfree(event);
+ return -EINVAL;
+}
+
+static int wsm_channel_switch_indication(struct cw1200_common *priv,
+ struct wsm_buf *buf)
+{
+ WARN_ON(WSM_GET32(buf));
+
+ priv->channel_switch_in_progress = 0;
+ wake_up(&priv->channel_switch_done);
+
+ wsm_unlock_tx(priv);
+
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+
+static int wsm_set_pm_indication(struct cw1200_common *priv,
+ struct wsm_buf *buf)
+{
+ /* TODO: Check buf (struct wsm_set_pm_complete) for validity */
+ if (priv->ps_mode_switch_in_progress) {
+ priv->ps_mode_switch_in_progress = 0;
+ wake_up(&priv->ps_mode_switch_done);
+ }
+ return 0;
+}
+
+static int wsm_scan_started(struct cw1200_common *priv, void *arg,
+ struct wsm_buf *buf)
+{
+ u32 status = WSM_GET32(buf);
+ if (status != WSM_STATUS_SUCCESS) {
+ cw1200_scan_failed_cb(priv);
+ return -EINVAL;
+ }
+ return 0;
+
+underflow:
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+static int wsm_scan_complete_indication(struct cw1200_common *priv,
+ struct wsm_buf *buf)
+{
+ struct wsm_scan_complete arg;
+ arg.status = WSM_GET32(buf);
+ arg.psm = WSM_GET8(buf);
+ arg.num_channels = WSM_GET8(buf);
+ cw1200_scan_complete_cb(priv, &arg);
+
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+
+static int wsm_join_complete_indication(struct cw1200_common *priv,
+ struct wsm_buf *buf)
+{
+ struct wsm_join_complete arg;
+ arg.status = WSM_GET32(buf);
+ pr_debug("[WSM] Join complete indication, status: %d\n", arg.status);
+ cw1200_join_complete_cb(priv, &arg);
+
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+
+static int wsm_find_complete_indication(struct cw1200_common *priv,
+ struct wsm_buf *buf)
+{
+ pr_warn("Implement find_complete_indication\n");
+ return 0;
+}
+
+static int wsm_ba_timeout_indication(struct cw1200_common *priv,
+ struct wsm_buf *buf)
+{
+ u32 dummy;
+ u8 tid;
+ u8 dummy2;
+ u8 addr[ETH_ALEN];
+
+ dummy = WSM_GET32(buf);
+ tid = WSM_GET8(buf);
+ dummy2 = WSM_GET8(buf);
+ WSM_GET(buf, addr, ETH_ALEN);
+
+ pr_info("BlockACK timeout, tid %d, addr %pM\n",
+ tid, addr);
+
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+
+static int wsm_suspend_resume_indication(struct cw1200_common *priv,
+ int link_id, struct wsm_buf *buf)
+{
+ u32 flags;
+ struct wsm_suspend_resume arg;
+
+ flags = WSM_GET32(buf);
+ arg.link_id = link_id;
+ arg.stop = !(flags & 1);
+ arg.multicast = !!(flags & 8);
+ arg.queue = (flags >> 1) & 3;
+
+ cw1200_suspend_resume(priv, &arg);
+
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+
+
+/* ******************************************************************** */
+/* WSM TX */
+
+static int wsm_cmd_send(struct cw1200_common *priv,
+ struct wsm_buf *buf,
+ void *arg, u16 cmd, long tmo)
+{
+ size_t buf_len = buf->data - buf->begin;
+ int ret;
+
+ /* Don't bother if we're dead. */
+ if (priv->bh_error) {
+ ret = 0;
+ goto done;
+ }
+
+ /* Block until the cmd buffer is completed. Tortuous. */
+ spin_lock(&priv->wsm_cmd.lock);
+ while (!priv->wsm_cmd.done) {
+ spin_unlock(&priv->wsm_cmd.lock);
+ spin_lock(&priv->wsm_cmd.lock);
+ }
+ priv->wsm_cmd.done = 0;
+ spin_unlock(&priv->wsm_cmd.lock);
+
+ if (cmd == WSM_WRITE_MIB_REQ_ID ||
+ cmd == WSM_READ_MIB_REQ_ID)
+ pr_debug("[WSM] >>> 0x%.4X [MIB: 0x%.4X] (%zu)\n",
+ cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]),
+ buf_len);
+ else
+ pr_debug("[WSM] >>> 0x%.4X (%zu)\n", cmd, buf_len);
+
+ /* Due to buggy SPI on CW1200, we need to
+ * pad the message by a few bytes to ensure
+ * that it's completely received.
+ */
+ buf_len += 4;
+
+ /* Fill HI message header */
+ /* BH will add sequence number */
+ ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len);
+ ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd);
+
+ spin_lock(&priv->wsm_cmd.lock);
+ BUG_ON(priv->wsm_cmd.ptr);
+ priv->wsm_cmd.ptr = buf->begin;
+ priv->wsm_cmd.len = buf_len;
+ priv->wsm_cmd.arg = arg;
+ priv->wsm_cmd.cmd = cmd;
+ spin_unlock(&priv->wsm_cmd.lock);
+
+ cw1200_bh_wakeup(priv);
+
+ /* Wait for command completion */
+ ret = wait_event_timeout(priv->wsm_cmd_wq,
+ priv->wsm_cmd.done, tmo);
+
+ if (!ret && !priv->wsm_cmd.done) {
+ spin_lock(&priv->wsm_cmd.lock);
+ priv->wsm_cmd.done = 1;
+ priv->wsm_cmd.ptr = NULL;
+ spin_unlock(&priv->wsm_cmd.lock);
+ if (priv->bh_error) {
+ /* Return ok to help system cleanup */
+ ret = 0;
+ } else {
+ pr_err("CMD req (0x%04x) stuck in firmware, killing BH\n", priv->wsm_cmd.cmd);
+ print_hex_dump_bytes("REQDUMP: ", DUMP_PREFIX_NONE,
+ buf->begin, buf_len);
+ pr_err("Outstanding outgoing frames: %d\n", priv->hw_bufs_used);
+
+ /* Kill BH thread to report the error to the top layer. */
+ atomic_add(1, &priv->bh_term);
+ wake_up(&priv->bh_wq);
+ ret = -ETIMEDOUT;
+ }
+ } else {
+ spin_lock(&priv->wsm_cmd.lock);
+ BUG_ON(!priv->wsm_cmd.done);
+ ret = priv->wsm_cmd.ret;
+ spin_unlock(&priv->wsm_cmd.lock);
+ }
+done:
+ wsm_buf_reset(buf);
+ return ret;
+}
+
+/* ******************************************************************** */
+/* WSM TX port control */
+
+void wsm_lock_tx(struct cw1200_common *priv)
+{
+ wsm_cmd_lock(priv);
+ if (atomic_add_return(1, &priv->tx_lock) == 1) {
+ if (wsm_flush_tx(priv))
+ pr_debug("[WSM] TX is locked.\n");
+ }
+ wsm_cmd_unlock(priv);
+}
+
+void wsm_lock_tx_async(struct cw1200_common *priv)
+{
+ if (atomic_add_return(1, &priv->tx_lock) == 1)
+ pr_debug("[WSM] TX is locked (async).\n");
+}
+
+bool wsm_flush_tx(struct cw1200_common *priv)
+{
+ unsigned long timestamp = jiffies;
+ bool pending = false;
+ long timeout;
+ int i;
+
+ /* Flush must be called with TX lock held. */
+ BUG_ON(!atomic_read(&priv->tx_lock));
+
+ /* First check if we really need to do something.
+ * It is safe to use unprotected access, as hw_bufs_used
+ * can only decrements.
+ */
+ if (!priv->hw_bufs_used)
+ return true;
+
+ if (priv->bh_error) {
+ /* In case of failure do not wait for magic. */
+ pr_err("[WSM] Fatal error occured, will not flush TX.\n");
+ return false;
+ } else {
+ /* Get a timestamp of "oldest" frame */
+ for (i = 0; i < 4; ++i)
+ pending |= cw1200_queue_get_xmit_timestamp(
+ &priv->tx_queue[i],
+ &timestamp, 0xffffffff);
+ /* If there's nothing pending, we're good */
+ if (!pending)
+ return true;
+
+ timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies;
+ if (timeout < 0 || wait_event_timeout(priv->bh_evt_wq,
+ !priv->hw_bufs_used,
+ timeout) <= 0) {
+ /* Hmmm... Not good. Frame had stuck in firmware. */
+ priv->bh_error = 1;
+ wiphy_err(priv->hw->wiphy, "[WSM] TX Frames (%d) stuck in firmware, killing BH\n", priv->hw_bufs_used);
+ wake_up(&priv->bh_wq);
+ return false;
+ }
+
+ /* Ok, everything is flushed. */
+ return true;
+ }
+}
+
+void wsm_unlock_tx(struct cw1200_common *priv)
+{
+ int tx_lock;
+ tx_lock = atomic_sub_return(1, &priv->tx_lock);
+ BUG_ON(tx_lock < 0);
+
+ if (tx_lock == 0) {
+ if (!priv->bh_error)
+ cw1200_bh_wakeup(priv);
+ pr_debug("[WSM] TX is unlocked.\n");
+ }
+}
+
+/* ******************************************************************** */
+/* WSM RX */
+
+int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len)
+{
+ struct wsm_buf buf;
+ u32 reason;
+ u32 reg[18];
+ char fname[48];
+ unsigned int i;
+
+ static const char * const reason_str[] = {
+ "undefined instruction",
+ "prefetch abort",
+ "data abort",
+ "unknown error",
+ };
+
+ buf.begin = buf.data = data;
+ buf.end = &buf.begin[len];
+
+ reason = WSM_GET32(&buf);
+ for (i = 0; i < ARRAY_SIZE(reg); ++i)
+ reg[i] = WSM_GET32(&buf);
+ WSM_GET(&buf, fname, sizeof(fname));
+
+ if (reason < 4)
+ wiphy_err(priv->hw->wiphy,
+ "Firmware exception: %s.\n",
+ reason_str[reason]);
+ else
+ wiphy_err(priv->hw->wiphy,
+ "Firmware assert at %.*s, line %d\n",
+ (int) sizeof(fname), fname, reg[1]);
+
+ for (i = 0; i < 12; i += 4)
+ wiphy_err(priv->hw->wiphy,
+ "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n",
+ i + 0, reg[i + 0], i + 1, reg[i + 1],
+ i + 2, reg[i + 2], i + 3, reg[i + 3]);
+ wiphy_err(priv->hw->wiphy,
+ "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n",
+ reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]);
+ i += 4;
+ wiphy_err(priv->hw->wiphy,
+ "CPSR: 0x%.8X, SPSR: 0x%.8X\n",
+ reg[i + 0], reg[i + 1]);
+
+ print_hex_dump_bytes("R1: ", DUMP_PREFIX_NONE,
+ fname, sizeof(fname));
+ return 0;
+
+underflow:
+ wiphy_err(priv->hw->wiphy, "Firmware exception.\n");
+ print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE,
+ data, len);
+ return -EINVAL;
+}
+
+int wsm_handle_rx(struct cw1200_common *priv, u16 id,
+ struct wsm_hdr *wsm, struct sk_buff **skb_p)
+{
+ int ret = 0;
+ struct wsm_buf wsm_buf;
+ int link_id = (id >> 6) & 0x0F;
+
+ /* Strip link id. */
+ id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX);
+
+ wsm_buf.begin = (u8 *)&wsm[0];
+ wsm_buf.data = (u8 *)&wsm[1];
+ wsm_buf.end = &wsm_buf.begin[__le32_to_cpu(wsm->len)];
+
+ pr_debug("[WSM] <<< 0x%.4X (%td)\n", id,
+ wsm_buf.end - wsm_buf.begin);
+
+ if (id == WSM_TX_CONFIRM_IND_ID) {
+ ret = wsm_tx_confirm(priv, &wsm_buf, link_id);
+ } else if (id == WSM_MULTI_TX_CONFIRM_ID) {
+ ret = wsm_multi_tx_confirm(priv, &wsm_buf, link_id);
+ } else if (id & 0x0400) {
+ void *wsm_arg;
+ u16 wsm_cmd;
+
+ /* Do not trust FW too much. Protection against repeated
+ * response and race condition removal (see above).
+ */
+ spin_lock(&priv->wsm_cmd.lock);
+ wsm_arg = priv->wsm_cmd.arg;
+ wsm_cmd = priv->wsm_cmd.cmd &
+ ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX);
+ priv->wsm_cmd.cmd = 0xFFFF;
+ spin_unlock(&priv->wsm_cmd.lock);
+
+ if (WARN_ON((id & ~0x0400) != wsm_cmd)) {
+ /* Note that any non-zero is a fatal retcode. */
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Note that wsm_arg can be NULL in case of timeout in
+ * wsm_cmd_send().
+ */
+
+ switch (id) {
+ case WSM_READ_MIB_RESP_ID:
+ if (wsm_arg)
+ ret = wsm_read_mib_confirm(priv, wsm_arg,
+ &wsm_buf);
+ break;
+ case WSM_WRITE_MIB_RESP_ID:
+ if (wsm_arg)
+ ret = wsm_write_mib_confirm(priv, wsm_arg,
+ &wsm_buf);
+ break;
+ case WSM_START_SCAN_RESP_ID:
+ if (wsm_arg)
+ ret = wsm_scan_started(priv, wsm_arg, &wsm_buf);
+ break;
+ case WSM_CONFIGURATION_RESP_ID:
+ if (wsm_arg)
+ ret = wsm_configuration_confirm(priv, wsm_arg,
+ &wsm_buf);
+ break;
+ case WSM_JOIN_RESP_ID:
+ if (wsm_arg)
+ ret = wsm_join_confirm(priv, wsm_arg, &wsm_buf);
+ break;
+ case WSM_STOP_SCAN_RESP_ID:
+ case WSM_RESET_RESP_ID:
+ case WSM_ADD_KEY_RESP_ID:
+ case WSM_REMOVE_KEY_RESP_ID:
+ case WSM_SET_PM_RESP_ID:
+ case WSM_SET_BSS_PARAMS_RESP_ID:
+ case 0x0412: /* set_tx_queue_params */
+ case WSM_EDCA_PARAMS_RESP_ID:
+ case WSM_SWITCH_CHANNEL_RESP_ID:
+ case WSM_START_RESP_ID:
+ case WSM_BEACON_TRANSMIT_RESP_ID:
+ case 0x0419: /* start_find */
+ case 0x041A: /* stop_find */
+ case 0x041B: /* update_ie */
+ case 0x041C: /* map_link */
+ WARN_ON(wsm_arg != NULL);
+ ret = wsm_generic_confirm(priv, wsm_arg, &wsm_buf);
+ if (ret) {
+ wiphy_warn(priv->hw->wiphy,
+ "wsm_generic_confirm failed for request 0x%04x.\n",
+ id & ~0x0400);
+
+ /* often 0x407 and 0x410 occur, this means we're dead.. */
+ if (priv->join_status >= CW1200_JOIN_STATUS_JOINING) {
+ wsm_lock_tx(priv);
+ if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+ }
+ }
+ break;
+ default:
+ wiphy_warn(priv->hw->wiphy,
+ "Unrecognized confirmation 0x%04x\n",
+ id & ~0x0400);
+ }
+
+ spin_lock(&priv->wsm_cmd.lock);
+ priv->wsm_cmd.ret = ret;
+ priv->wsm_cmd.done = 1;
+ spin_unlock(&priv->wsm_cmd.lock);
+
+ ret = 0; /* Error response from device should ne stop BH. */
+
+ wake_up(&priv->wsm_cmd_wq);
+ } else if (id & 0x0800) {
+ switch (id) {
+ case WSM_STARTUP_IND_ID:
+ ret = wsm_startup_indication(priv, &wsm_buf);
+ break;
+ case WSM_RECEIVE_IND_ID:
+ ret = wsm_receive_indication(priv, link_id,
+ &wsm_buf, skb_p);
+ break;
+ case 0x0805:
+ ret = wsm_event_indication(priv, &wsm_buf);
+ break;
+ case WSM_SCAN_COMPLETE_IND_ID:
+ ret = wsm_scan_complete_indication(priv, &wsm_buf);
+ break;
+ case 0x0808:
+ ret = wsm_ba_timeout_indication(priv, &wsm_buf);
+ break;
+ case 0x0809:
+ ret = wsm_set_pm_indication(priv, &wsm_buf);
+ break;
+ case 0x080A:
+ ret = wsm_channel_switch_indication(priv, &wsm_buf);
+ break;
+ case 0x080B:
+ ret = wsm_find_complete_indication(priv, &wsm_buf);
+ break;
+ case 0x080C:
+ ret = wsm_suspend_resume_indication(priv,
+ link_id, &wsm_buf);
+ break;
+ case 0x080F:
+ ret = wsm_join_complete_indication(priv, &wsm_buf);
+ break;
+ default:
+ pr_warn("Unrecognised WSM ID %04x\n", id);
+ }
+ } else {
+ WARN_ON(1);
+ ret = -EINVAL;
+ }
+out:
+ return ret;
+}
+
+static bool wsm_handle_tx_data(struct cw1200_common *priv,
+ struct wsm_tx *wsm,
+ const struct ieee80211_tx_info *tx_info,
+ const struct cw1200_txpriv *txpriv,
+ struct cw1200_queue *queue)
+{
+ bool handled = false;
+ const struct ieee80211_hdr *frame =
+ (struct ieee80211_hdr *)&((u8 *)wsm)[txpriv->offset];
+ __le16 fctl = frame->frame_control;
+ enum {
+ do_probe,
+ do_drop,
+ do_wep,
+ do_tx,
+ } action = do_tx;
+
+ switch (priv->mode) {
+ case NL80211_IFTYPE_STATION:
+ if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
+ action = do_tx;
+ else if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA)
+ action = do_drop;
+ break;
+ case NL80211_IFTYPE_AP:
+ if (!priv->join_status) {
+ action = do_drop;
+ } else if (!(BIT(txpriv->raw_link_id) &
+ (BIT(0) | priv->link_id_map))) {
+ wiphy_warn(priv->hw->wiphy,
+ "A frame with expired link id is dropped.\n");
+ action = do_drop;
+ }
+ if (cw1200_queue_get_generation(wsm->packet_id) >
+ CW1200_MAX_REQUEUE_ATTEMPTS) {
+ /* HACK!!! WSM324 firmware has tendency to requeue
+ * multicast frames in a loop, causing performance
+ * drop and high power consumption of the driver.
+ * In this situation it is better just to drop
+ * the problematic frame.
+ */
+ wiphy_warn(priv->hw->wiphy,
+ "Too many attempts to requeue a frame; dropped.\n");
+ action = do_drop;
+ }
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ if (priv->join_status != CW1200_JOIN_STATUS_IBSS)
+ action = do_drop;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ action = do_tx; /* TODO: Test me! */
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ default:
+ action = do_drop;
+ break;
+ }
+
+ if (action == do_tx) {
+ if (ieee80211_is_nullfunc(fctl)) {
+ spin_lock(&priv->bss_loss_lock);
+ if (priv->bss_loss_state) {
+ priv->bss_loss_confirm_id = wsm->packet_id;
+ wsm->queue_id = WSM_QUEUE_VOICE;
+ }
+ spin_unlock(&priv->bss_loss_lock);
+ } else if (ieee80211_is_probe_req(fctl)) {
+ action = do_probe;
+ } else if (ieee80211_is_deauth(fctl) &&
+ priv->mode != NL80211_IFTYPE_AP) {
+ pr_debug("[WSM] Issue unjoin command due to tx deauth.\n");
+ wsm_lock_tx_async(priv);
+ if (queue_work(priv->workqueue,
+ &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+ } else if (ieee80211_has_protected(fctl) &&
+ tx_info->control.hw_key &&
+ tx_info->control.hw_key->keyidx != priv->wep_default_key_id &&
+ (tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP104)) {
+ action = do_wep;
+ }
+ }
+
+ switch (action) {
+ case do_probe:
+ /* An interesting FW "feature". Device filters probe responses.
+ * The easiest way to get it back is to convert
+ * probe request into WSM start_scan command.
+ */
+ pr_debug("[WSM] Convert probe request to scan.\n");
+ wsm_lock_tx_async(priv);
+ priv->pending_frame_id = __le32_to_cpu(wsm->packet_id);
+ if (queue_delayed_work(priv->workqueue,
+ &priv->scan.probe_work, 0) <= 0)
+ wsm_unlock_tx(priv);
+ handled = true;
+ break;
+ case do_drop:
+ pr_debug("[WSM] Drop frame (0x%.4X).\n", fctl);
+ BUG_ON(cw1200_queue_remove(queue,
+ __le32_to_cpu(wsm->packet_id)));
+ handled = true;
+ break;
+ case do_wep:
+ pr_debug("[WSM] Issue set_default_wep_key.\n");
+ wsm_lock_tx_async(priv);
+ priv->wep_default_key_id = tx_info->control.hw_key->keyidx;
+ priv->pending_frame_id = __le32_to_cpu(wsm->packet_id);
+ if (queue_work(priv->workqueue, &priv->wep_key_work) <= 0)
+ wsm_unlock_tx(priv);
+ handled = true;
+ break;
+ case do_tx:
+ pr_debug("[WSM] Transmit frame.\n");
+ break;
+ default:
+ /* Do nothing */
+ break;
+ }
+ return handled;
+}
+
+static int cw1200_get_prio_queue(struct cw1200_common *priv,
+ u32 link_id_map, int *total)
+{
+ static const int urgent = BIT(CW1200_LINK_ID_AFTER_DTIM) |
+ BIT(CW1200_LINK_ID_UAPSD);
+ struct wsm_edca_queue_params *edca;
+ unsigned score, best = -1;
+ int winner = -1;
+ int queued;
+ int i;
+
+ /* search for a winner using edca params */
+ for (i = 0; i < 4; ++i) {
+ queued = cw1200_queue_get_num_queued(&priv->tx_queue[i],
+ link_id_map);
+ if (!queued)
+ continue;
+ *total += queued;
+ edca = &priv->edca.params[i];
+ score = ((edca->aifns + edca->cwmin) << 16) +
+ ((edca->cwmax - edca->cwmin) *
+ (get_random_int() & 0xFFFF));
+ if (score < best && (winner < 0 || i != 3)) {
+ best = score;
+ winner = i;
+ }
+ }
+
+ /* override winner if bursting */
+ if (winner >= 0 && priv->tx_burst_idx >= 0 &&
+ winner != priv->tx_burst_idx &&
+ !cw1200_queue_get_num_queued(
+ &priv->tx_queue[winner],
+ link_id_map & urgent) &&
+ cw1200_queue_get_num_queued(
+ &priv->tx_queue[priv->tx_burst_idx],
+ link_id_map))
+ winner = priv->tx_burst_idx;
+
+ return winner;
+}
+
+static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv,
+ struct cw1200_queue **queue_p,
+ u32 *tx_allowed_mask_p,
+ bool *more)
+{
+ int idx;
+ u32 tx_allowed_mask;
+ int total = 0;
+
+ /* Search for a queue with multicast frames buffered */
+ if (priv->tx_multicast) {
+ tx_allowed_mask = BIT(CW1200_LINK_ID_AFTER_DTIM);
+ idx = cw1200_get_prio_queue(priv,
+ tx_allowed_mask, &total);
+ if (idx >= 0) {
+ *more = total > 1;
+ goto found;
+ }
+ }
+
+ /* Search for unicast traffic */
+ tx_allowed_mask = ~priv->sta_asleep_mask;
+ tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD);
+ if (priv->sta_asleep_mask) {
+ tx_allowed_mask |= priv->pspoll_mask;
+ tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM);
+ } else {
+ tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM);
+ }
+ idx = cw1200_get_prio_queue(priv,
+ tx_allowed_mask, &total);
+ if (idx < 0)
+ return -ENOENT;
+
+found:
+ *queue_p = &priv->tx_queue[idx];
+ *tx_allowed_mask_p = tx_allowed_mask;
+ return 0;
+}
+
+int wsm_get_tx(struct cw1200_common *priv, u8 **data,
+ size_t *tx_len, int *burst)
+{
+ struct wsm_tx *wsm = NULL;
+ struct ieee80211_tx_info *tx_info;
+ struct cw1200_queue *queue = NULL;
+ int queue_num;
+ u32 tx_allowed_mask = 0;
+ const struct cw1200_txpriv *txpriv = NULL;
+ int count = 0;
+
+ /* More is used only for broadcasts. */
+ bool more = false;
+
+ if (priv->wsm_cmd.ptr) { /* CMD request */
+ ++count;
+ spin_lock(&priv->wsm_cmd.lock);
+ BUG_ON(!priv->wsm_cmd.ptr);
+ *data = priv->wsm_cmd.ptr;
+ *tx_len = priv->wsm_cmd.len;
+ *burst = 1;
+ spin_unlock(&priv->wsm_cmd.lock);
+ } else {
+ for (;;) {
+ int ret;
+
+ if (atomic_add_return(0, &priv->tx_lock))
+ break;
+
+ spin_lock_bh(&priv->ps_state_lock);
+
+ ret = wsm_get_tx_queue_and_mask(priv, &queue,
+ &tx_allowed_mask, &more);
+ queue_num = queue - priv->tx_queue;
+
+ if (priv->buffered_multicasts &&
+ (ret || !more) &&
+ (priv->tx_multicast || !priv->sta_asleep_mask)) {
+ priv->buffered_multicasts = false;
+ if (priv->tx_multicast) {
+ priv->tx_multicast = false;
+ queue_work(priv->workqueue,
+ &priv->multicast_stop_work);
+ }
+ }
+
+ spin_unlock_bh(&priv->ps_state_lock);
+
+ if (ret)
+ break;
+
+ if (cw1200_queue_get(queue,
+ tx_allowed_mask,
+ &wsm, &tx_info, &txpriv))
+ continue;
+
+ if (wsm_handle_tx_data(priv, wsm,
+ tx_info, txpriv, queue))
+ continue; /* Handled by WSM */
+
+ wsm->hdr.id &= __cpu_to_le16(
+ ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX));
+ wsm->hdr.id |= cpu_to_le16(
+ WSM_TX_LINK_ID(txpriv->raw_link_id));
+ priv->pspoll_mask &= ~BIT(txpriv->raw_link_id);
+
+ *data = (u8 *)wsm;
+ *tx_len = __le16_to_cpu(wsm->hdr.len);
+
+ /* allow bursting if txop is set */
+ if (priv->edca.params[queue_num].txop_limit)
+ *burst = min(*burst,
+ (int)cw1200_queue_get_num_queued(queue, tx_allowed_mask) + 1);
+ else
+ *burst = 1;
+
+ /* store index of bursting queue */
+ if (*burst > 1)
+ priv->tx_burst_idx = queue_num;
+ else
+ priv->tx_burst_idx = -1;
+
+ if (more) {
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *)
+ &((u8 *)wsm)[txpriv->offset];
+ /* more buffered multicast/broadcast frames
+ * ==> set MoreData flag in IEEE 802.11 header
+ * to inform PS STAs
+ */
+ hdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ }
+
+ pr_debug("[WSM] >>> 0x%.4X (%zu) %p %c\n",
+ 0x0004, *tx_len, *data,
+ wsm->more ? 'M' : ' ');
+ ++count;
+ break;
+ }
+ }
+
+ return count;
+}
+
+void wsm_txed(struct cw1200_common *priv, u8 *data)
+{
+ if (data == priv->wsm_cmd.ptr) {
+ spin_lock(&priv->wsm_cmd.lock);
+ priv->wsm_cmd.ptr = NULL;
+ spin_unlock(&priv->wsm_cmd.lock);
+ }
+}
+
+/* ******************************************************************** */
+/* WSM buffer */
+
+void wsm_buf_init(struct wsm_buf *buf)
+{
+ BUG_ON(buf->begin);
+ buf->begin = kmalloc(FWLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);
+ buf->end = buf->begin ? &buf->begin[FWLOAD_BLOCK_SIZE] : buf->begin;
+ wsm_buf_reset(buf);
+}
+
+void wsm_buf_deinit(struct wsm_buf *buf)
+{
+ kfree(buf->begin);
+ buf->begin = buf->data = buf->end = NULL;
+}
+
+static void wsm_buf_reset(struct wsm_buf *buf)
+{
+ if (buf->begin) {
+ buf->data = &buf->begin[4];
+ *(u32 *)buf->begin = 0;
+ } else {
+ buf->data = buf->begin;
+ }
+}
+
+static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size)
+{
+ size_t pos = buf->data - buf->begin;
+ size_t size = pos + extra_size;
+
+ size = round_up(size, FWLOAD_BLOCK_SIZE);
+
+ buf->begin = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA);
+ if (buf->begin) {
+ buf->data = &buf->begin[pos];
+ buf->end = &buf->begin[size];
+ return 0;
+ } else {
+ buf->end = buf->data = buf->begin;
+ return -ENOMEM;
+ }
+}
diff --git a/drivers/net/wireless/cw1200/wsm.h b/drivers/net/wireless/cw1200/wsm.h
new file mode 100644
index 000000000000..2816171f7a1d
--- /dev/null
+++ b/drivers/net/wireless/cw1200/wsm.h
@@ -0,0 +1,1873 @@
+/*
+ * WSM host interface (HI) interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on CW1200 UMAC WSM API, which is
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Stewart Mathers <stewart.mathers@stericsson.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 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef CW1200_WSM_H_INCLUDED
+#define CW1200_WSM_H_INCLUDED
+
+#include <linux/spinlock.h>
+
+struct cw1200_common;
+
+/* Bands */
+/* Radio band 2.412 -2.484 GHz. */
+#define WSM_PHY_BAND_2_4G (0)
+
+/* Radio band 4.9375-5.8250 GHz. */
+#define WSM_PHY_BAND_5G (1)
+
+/* Transmit rates */
+/* 1 Mbps ERP-DSSS */
+#define WSM_TRANSMIT_RATE_1 (0)
+
+/* 2 Mbps ERP-DSSS */
+#define WSM_TRANSMIT_RATE_2 (1)
+
+/* 5.5 Mbps ERP-CCK */
+#define WSM_TRANSMIT_RATE_5 (2)
+
+/* 11 Mbps ERP-CCK */
+#define WSM_TRANSMIT_RATE_11 (3)
+
+/* 22 Mbps ERP-PBCC (Not supported) */
+/* #define WSM_TRANSMIT_RATE_22 (4) */
+
+/* 33 Mbps ERP-PBCC (Not supported) */
+/* #define WSM_TRANSMIT_RATE_33 (5) */
+
+/* 6 Mbps (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_6 (6)
+
+/* 9 Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_9 (7)
+
+/* 12 Mbps (6 Mbps) ERP-OFDM, QPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_12 (8)
+
+/* 18 Mbps (9 Mbps) ERP-OFDM, QPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_18 (9)
+
+/* 24 Mbps (12 Mbps) ERP-OFDM, 16QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_24 (10)
+
+/* 36 Mbps (18 Mbps) ERP-OFDM, 16QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_36 (11)
+
+/* 48 Mbps (24 Mbps) ERP-OFDM, 64QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_48 (12)
+
+/* 54 Mbps (27 Mbps) ERP-OFDM, 64QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_54 (13)
+
+/* 6.5 Mbps HT-OFDM, BPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_6 (14)
+
+/* 13 Mbps HT-OFDM, QPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_13 (15)
+
+/* 19.5 Mbps HT-OFDM, QPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_19 (16)
+
+/* 26 Mbps HT-OFDM, 16QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_26 (17)
+
+/* 39 Mbps HT-OFDM, 16QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_39 (18)
+
+/* 52 Mbps HT-OFDM, 64QAM coding rate 2/3 */
+#define WSM_TRANSMIT_RATE_HT_52 (19)
+
+/* 58.5 Mbps HT-OFDM, 64QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_58 (20)
+
+/* 65 Mbps HT-OFDM, 64QAM coding rate 5/6 */
+#define WSM_TRANSMIT_RATE_HT_65 (21)
+
+/* Scan types */
+/* Foreground scan */
+#define WSM_SCAN_TYPE_FOREGROUND (0)
+
+/* Background scan */
+#define WSM_SCAN_TYPE_BACKGROUND (1)
+
+/* Auto scan */
+#define WSM_SCAN_TYPE_AUTO (2)
+
+/* Scan flags */
+/* Forced background scan means if the station cannot */
+/* enter the power-save mode, it shall force to perform a */
+/* background scan. Only valid when ScanType is */
+/* background scan. */
+#define WSM_SCAN_FLAG_FORCE_BACKGROUND (BIT(0))
+
+/* The WLAN device scans one channel at a time so */
+/* that disturbance to the data traffic is minimized. */
+#define WSM_SCAN_FLAG_SPLIT_METHOD (BIT(1))
+
+/* Preamble Type. Long if not set. */
+#define WSM_SCAN_FLAG_SHORT_PREAMBLE (BIT(2))
+
+/* 11n Tx Mode. Mixed if not set. */
+#define WSM_SCAN_FLAG_11N_GREENFIELD (BIT(3))
+
+/* Scan constraints */
+/* Maximum number of channels to be scanned. */
+#define WSM_SCAN_MAX_NUM_OF_CHANNELS (48)
+
+/* The maximum number of SSIDs that the device can scan for. */
+#define WSM_SCAN_MAX_NUM_OF_SSIDS (2)
+
+/* Power management modes */
+/* 802.11 Active mode */
+#define WSM_PSM_ACTIVE (0)
+
+/* 802.11 PS mode */
+#define WSM_PSM_PS BIT(0)
+
+/* Fast Power Save bit */
+#define WSM_PSM_FAST_PS_FLAG BIT(7)
+
+/* Dynamic aka Fast power save */
+#define WSM_PSM_FAST_PS (BIT(0) | BIT(7))
+
+/* Undetermined */
+/* Note : Undetermined status is reported when the */
+/* NULL data frame used to advertise the PM mode to */
+/* the AP at Pre or Post Background Scan is not Acknowledged */
+#define WSM_PSM_UNKNOWN BIT(1)
+
+/* Queue IDs */
+/* best effort/legacy */
+#define WSM_QUEUE_BEST_EFFORT (0)
+
+/* background */
+#define WSM_QUEUE_BACKGROUND (1)
+
+/* video */
+#define WSM_QUEUE_VIDEO (2)
+
+/* voice */
+#define WSM_QUEUE_VOICE (3)
+
+/* HT TX parameters */
+/* Non-HT */
+#define WSM_HT_TX_NON_HT (0)
+
+/* Mixed format */
+#define WSM_HT_TX_MIXED (1)
+
+/* Greenfield format */
+#define WSM_HT_TX_GREENFIELD (2)
+
+/* STBC allowed */
+#define WSM_HT_TX_STBC (BIT(7))
+
+/* EPTA prioirty flags for BT Coex */
+/* default epta priority */
+#define WSM_EPTA_PRIORITY_DEFAULT 4
+/* use for normal data */
+#define WSM_EPTA_PRIORITY_DATA 4
+/* use for connect/disconnect/roaming*/
+#define WSM_EPTA_PRIORITY_MGT 5
+/* use for action frames */
+#define WSM_EPTA_PRIORITY_ACTION 5
+/* use for AC_VI data */
+#define WSM_EPTA_PRIORITY_VIDEO 5
+/* use for AC_VO data */
+#define WSM_EPTA_PRIORITY_VOICE 6
+/* use for EAPOL exchange */
+#define WSM_EPTA_PRIORITY_EAPOL 7
+
+/* TX status */
+/* Frame was sent aggregated */
+/* Only valid for WSM_SUCCESS status. */
+#define WSM_TX_STATUS_AGGREGATION (BIT(0))
+
+/* Host should requeue this frame later. */
+/* Valid only when status is WSM_REQUEUE. */
+#define WSM_TX_STATUS_REQUEUE (BIT(1))
+
+/* Normal Ack */
+#define WSM_TX_STATUS_NORMAL_ACK (0<<2)
+
+/* No Ack */
+#define WSM_TX_STATUS_NO_ACK (1<<2)
+
+/* No explicit acknowledgement */
+#define WSM_TX_STATUS_NO_EXPLICIT_ACK (2<<2)
+
+/* Block Ack */
+/* Only valid for WSM_SUCCESS status. */
+#define WSM_TX_STATUS_BLOCK_ACK (3<<2)
+
+/* RX status */
+/* Unencrypted */
+#define WSM_RX_STATUS_UNENCRYPTED (0<<0)
+
+/* WEP */
+#define WSM_RX_STATUS_WEP (1<<0)
+
+/* TKIP */
+#define WSM_RX_STATUS_TKIP (2<<0)
+
+/* AES */
+#define WSM_RX_STATUS_AES (3<<0)
+
+/* WAPI */
+#define WSM_RX_STATUS_WAPI (4<<0)
+
+/* Macro to fetch encryption subfield. */
+#define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07)
+
+/* Frame was part of an aggregation */
+#define WSM_RX_STATUS_AGGREGATE (BIT(3))
+
+/* Frame was first in the aggregation */
+#define WSM_RX_STATUS_AGGREGATE_FIRST (BIT(4))
+
+/* Frame was last in the aggregation */
+#define WSM_RX_STATUS_AGGREGATE_LAST (BIT(5))
+
+/* Indicates a defragmented frame */
+#define WSM_RX_STATUS_DEFRAGMENTED (BIT(6))
+
+/* Indicates a Beacon frame */
+#define WSM_RX_STATUS_BEACON (BIT(7))
+
+/* Indicates STA bit beacon TIM field */
+#define WSM_RX_STATUS_TIM (BIT(8))
+
+/* Indicates Beacon frame's virtual bitmap contains multicast bit */
+#define WSM_RX_STATUS_MULTICAST (BIT(9))
+
+/* Indicates frame contains a matching SSID */
+#define WSM_RX_STATUS_MATCHING_SSID (BIT(10))
+
+/* Indicates frame contains a matching BSSI */
+#define WSM_RX_STATUS_MATCHING_BSSI (BIT(11))
+
+/* Indicates More bit set in Framectl field */
+#define WSM_RX_STATUS_MORE_DATA (BIT(12))
+
+/* Indicates frame received during a measurement process */
+#define WSM_RX_STATUS_MEASUREMENT (BIT(13))
+
+/* Indicates frame received as an HT packet */
+#define WSM_RX_STATUS_HT (BIT(14))
+
+/* Indicates frame received with STBC */
+#define WSM_RX_STATUS_STBC (BIT(15))
+
+/* Indicates Address 1 field matches dot11StationId */
+#define WSM_RX_STATUS_ADDRESS1 (BIT(16))
+
+/* Indicates Group address present in the Address 1 field */
+#define WSM_RX_STATUS_GROUP (BIT(17))
+
+/* Indicates Broadcast address present in the Address 1 field */
+#define WSM_RX_STATUS_BROADCAST (BIT(18))
+
+/* Indicates group key used with encrypted frames */
+#define WSM_RX_STATUS_GROUP_KEY (BIT(19))
+
+/* Macro to fetch encryption key index. */
+#define WSM_RX_STATUS_KEY_IDX(status) (((status >> 20)) & 0x0F)
+
+/* Indicates TSF inclusion after 802.11 frame body */
+#define WSM_RX_STATUS_TSF_INCLUDED (BIT(24))
+
+/* Frame Control field starts at Frame offset + 2 */
+#define WSM_TX_2BYTES_SHIFT (BIT(7))
+
+/* Join mode */
+/* IBSS */
+#define WSM_JOIN_MODE_IBSS (0)
+
+/* BSS */
+#define WSM_JOIN_MODE_BSS (1)
+
+/* PLCP preamble type */
+/* For long preamble */
+#define WSM_JOIN_PREAMBLE_LONG (0)
+
+/* For short preamble (Long for 1Mbps) */
+#define WSM_JOIN_PREAMBLE_SHORT (1)
+
+/* For short preamble (Long for 1 and 2Mbps) */
+#define WSM_JOIN_PREAMBLE_SHORT_2 (2)
+
+/* Join flags */
+/* Unsynchronized */
+#define WSM_JOIN_FLAGS_UNSYNCRONIZED BIT(0)
+/* The BSS owner is a P2P GO */
+#define WSM_JOIN_FLAGS_P2P_GO BIT(1)
+/* Force to join BSS with the BSSID and the
+ * SSID specified without waiting for beacons. The
+ * ProbeForJoin parameter is ignored.
+ */
+#define WSM_JOIN_FLAGS_FORCE BIT(2)
+/* Give probe request/response higher
+ * priority over the BT traffic
+ */
+#define WSM_JOIN_FLAGS_PRIO BIT(3)
+/* Issue immediate join confirmation and use
+ * join complete to notify about completion
+ */
+#define WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND BIT(5)
+
+/* Key types */
+#define WSM_KEY_TYPE_WEP_DEFAULT (0)
+#define WSM_KEY_TYPE_WEP_PAIRWISE (1)
+#define WSM_KEY_TYPE_TKIP_GROUP (2)
+#define WSM_KEY_TYPE_TKIP_PAIRWISE (3)
+#define WSM_KEY_TYPE_AES_GROUP (4)
+#define WSM_KEY_TYPE_AES_PAIRWISE (5)
+#define WSM_KEY_TYPE_WAPI_GROUP (6)
+#define WSM_KEY_TYPE_WAPI_PAIRWISE (7)
+
+/* Key indexes */
+#define WSM_KEY_MAX_INDEX (10)
+
+/* ACK policy */
+#define WSM_ACK_POLICY_NORMAL (0)
+#define WSM_ACK_POLICY_NO_ACK (1)
+
+/* Start modes */
+#define WSM_START_MODE_AP (0) /* Mini AP */
+#define WSM_START_MODE_P2P_GO (1) /* P2P GO */
+#define WSM_START_MODE_P2P_DEV (2) /* P2P device */
+
+/* SetAssociationMode MIB flags */
+#define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE (BIT(0))
+#define WSM_ASSOCIATION_MODE_USE_HT_MODE (BIT(1))
+#define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET (BIT(2))
+#define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING (BIT(3))
+#define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES (BIT(4))
+
+/* RcpiRssiThreshold MIB flags */
+#define WSM_RCPI_RSSI_THRESHOLD_ENABLE (BIT(0))
+#define WSM_RCPI_RSSI_USE_RSSI (BIT(1))
+#define WSM_RCPI_RSSI_DONT_USE_UPPER (BIT(2))
+#define WSM_RCPI_RSSI_DONT_USE_LOWER (BIT(3))
+
+/* Update-ie constants */
+#define WSM_UPDATE_IE_BEACON (BIT(0))
+#define WSM_UPDATE_IE_PROBE_RESP (BIT(1))
+#define WSM_UPDATE_IE_PROBE_REQ (BIT(2))
+
+/* WSM events */
+/* Error */
+#define WSM_EVENT_ERROR (0)
+
+/* BSS lost */
+#define WSM_EVENT_BSS_LOST (1)
+
+/* BSS regained */
+#define WSM_EVENT_BSS_REGAINED (2)
+
+/* Radar detected */
+#define WSM_EVENT_RADAR_DETECTED (3)
+
+/* RCPI or RSSI threshold triggered */
+#define WSM_EVENT_RCPI_RSSI (4)
+
+/* BT inactive */
+#define WSM_EVENT_BT_INACTIVE (5)
+
+/* BT active */
+#define WSM_EVENT_BT_ACTIVE (6)
+
+/* MIB IDs */
+/* 4.1 dot11StationId */
+#define WSM_MIB_ID_DOT11_STATION_ID 0x0000
+
+/* 4.2 dot11MaxtransmitMsduLifeTime */
+#define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME 0x0001
+
+/* 4.3 dot11MaxReceiveLifeTime */
+#define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME 0x0002
+
+/* 4.4 dot11SlotTime */
+#define WSM_MIB_ID_DOT11_SLOT_TIME 0x0003
+
+/* 4.5 dot11GroupAddressesTable */
+#define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004
+#define WSM_MAX_GRP_ADDRTABLE_ENTRIES 8
+
+/* 4.6 dot11WepDefaultKeyId */
+#define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID 0x0005
+
+/* 4.7 dot11CurrentTxPowerLevel */
+#define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL 0x0006
+
+/* 4.8 dot11RTSThreshold */
+#define WSM_MIB_ID_DOT11_RTS_THRESHOLD 0x0007
+
+/* 4.9 NonErpProtection */
+#define WSM_MIB_ID_NON_ERP_PROTECTION 0x1000
+
+/* 4.10 ArpIpAddressesTable */
+#define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE 0x1001
+#define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES 1
+
+/* 4.11 TemplateFrame */
+#define WSM_MIB_ID_TEMPLATE_FRAME 0x1002
+
+/* 4.12 RxFilter */
+#define WSM_MIB_ID_RX_FILTER 0x1003
+
+/* 4.13 BeaconFilterTable */
+#define WSM_MIB_ID_BEACON_FILTER_TABLE 0x1004
+
+/* 4.14 BeaconFilterEnable */
+#define WSM_MIB_ID_BEACON_FILTER_ENABLE 0x1005
+
+/* 4.15 OperationalPowerMode */
+#define WSM_MIB_ID_OPERATIONAL_POWER_MODE 0x1006
+
+/* 4.16 BeaconWakeUpPeriod */
+#define WSM_MIB_ID_BEACON_WAKEUP_PERIOD 0x1007
+
+/* 4.17 RcpiRssiThreshold */
+#define WSM_MIB_ID_RCPI_RSSI_THRESHOLD 0x1009
+
+/* 4.18 StatisticsTable */
+#define WSM_MIB_ID_STATISTICS_TABLE 0x100A
+
+/* 4.19 IbssPsConfig */
+#define WSM_MIB_ID_IBSS_PS_CONFIG 0x100B
+
+/* 4.20 CountersTable */
+#define WSM_MIB_ID_COUNTERS_TABLE 0x100C
+
+/* 4.21 BlockAckPolicy */
+#define WSM_MIB_ID_BLOCK_ACK_POLICY 0x100E
+
+/* 4.22 OverrideInternalTxRate */
+#define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE 0x100F
+
+/* 4.23 SetAssociationMode */
+#define WSM_MIB_ID_SET_ASSOCIATION_MODE 0x1010
+
+/* 4.24 UpdateEptaConfigData */
+#define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA 0x1011
+
+/* 4.25 SelectCcaMethod */
+#define WSM_MIB_ID_SELECT_CCA_METHOD 0x1012
+
+/* 4.26 SetUpasdInformation */
+#define WSM_MIB_ID_SET_UAPSD_INFORMATION 0x1013
+
+/* 4.27 SetAutoCalibrationMode WBF00004073 */
+#define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE 0x1015
+
+/* 4.28 SetTxRateRetryPolicy */
+#define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY 0x1016
+
+/* 4.29 SetHostMessageTypeFilter */
+#define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER 0x1017
+
+/* 4.30 P2PFindInfo */
+#define WSM_MIB_ID_P2P_FIND_INFO 0x1018
+
+/* 4.31 P2PPsModeInfo */
+#define WSM_MIB_ID_P2P_PS_MODE_INFO 0x1019
+
+/* 4.32 SetEtherTypeDataFrameFilter */
+#define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A
+
+/* 4.33 SetUDPPortDataFrameFilter */
+#define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER 0x101B
+
+/* 4.34 SetMagicDataFrameFilter */
+#define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER 0x101C
+
+/* 4.35 P2PDeviceInfo */
+#define WSM_MIB_ID_P2P_DEVICE_INFO 0x101D
+
+/* 4.36 SetWCDMABand */
+#define WSM_MIB_ID_SET_WCDMA_BAND 0x101E
+
+/* 4.37 GroupTxSequenceCounter */
+#define WSM_MIB_ID_GRP_SEQ_COUNTER 0x101F
+
+/* 4.38 ProtectedMgmtPolicy */
+#define WSM_MIB_ID_PROTECTED_MGMT_POLICY 0x1020
+
+/* 4.39 SetHtProtection */
+#define WSM_MIB_ID_SET_HT_PROTECTION 0x1021
+
+/* 4.40 GPIO Command */
+#define WSM_MIB_ID_GPIO_COMMAND 0x1022
+
+/* 4.41 TSF Counter Value */
+#define WSM_MIB_ID_TSF_COUNTER 0x1023
+
+/* Test Purposes Only */
+#define WSM_MIB_ID_BLOCK_ACK_INFO 0x100D
+
+/* 4.42 UseMultiTxConfMessage */
+#define WSM_MIB_USE_MULTI_TX_CONF 0x1024
+
+/* 4.43 Keep-alive period */
+#define WSM_MIB_ID_KEEP_ALIVE_PERIOD 0x1025
+
+/* 4.44 Disable BSSID filter */
+#define WSM_MIB_ID_DISABLE_BSSID_FILTER 0x1026
+
+/* Frame template types */
+#define WSM_FRAME_TYPE_PROBE_REQUEST (0)
+#define WSM_FRAME_TYPE_BEACON (1)
+#define WSM_FRAME_TYPE_NULL (2)
+#define WSM_FRAME_TYPE_QOS_NULL (3)
+#define WSM_FRAME_TYPE_PS_POLL (4)
+#define WSM_FRAME_TYPE_PROBE_RESPONSE (5)
+
+#define WSM_FRAME_GREENFIELD (0x80) /* See 4.11 */
+
+/* Status */
+/* The WSM firmware has completed a request */
+/* successfully. */
+#define WSM_STATUS_SUCCESS (0)
+
+/* This is a generic failure code if other error codes do */
+/* not apply. */
+#define WSM_STATUS_FAILURE (1)
+
+/* A request contains one or more invalid parameters. */
+#define WSM_INVALID_PARAMETER (2)
+
+/* The request cannot perform because the device is in */
+/* an inappropriate mode. */
+#define WSM_ACCESS_DENIED (3)
+
+/* The frame received includes a decryption error. */
+#define WSM_STATUS_DECRYPTFAILURE (4)
+
+/* A MIC failure is detected in the received packets. */
+#define WSM_STATUS_MICFAILURE (5)
+
+/* The transmit request failed due to retry limit being */
+/* exceeded. */
+#define WSM_STATUS_RETRY_EXCEEDED (6)
+
+/* The transmit request failed due to MSDU life time */
+/* being exceeded. */
+#define WSM_STATUS_TX_LIFETIME_EXCEEDED (7)
+
+/* The link to the AP is lost. */
+#define WSM_STATUS_LINK_LOST (8)
+
+/* No key was found for the encrypted frame */
+#define WSM_STATUS_NO_KEY_FOUND (9)
+
+/* Jammer was detected when transmitting this frame */
+#define WSM_STATUS_JAMMER_DETECTED (10)
+
+/* The message should be requeued later. */
+/* This is applicable only to Transmit */
+#define WSM_REQUEUE (11)
+
+/* Advanced filtering options */
+#define WSM_MAX_FILTER_ELEMENTS (4)
+
+#define WSM_FILTER_ACTION_IGNORE (0)
+#define WSM_FILTER_ACTION_FILTER_IN (1)
+#define WSM_FILTER_ACTION_FILTER_OUT (2)
+
+#define WSM_FILTER_PORT_TYPE_DST (0)
+#define WSM_FILTER_PORT_TYPE_SRC (1)
+
+/* Actual header of WSM messages */
+struct wsm_hdr {
+ __le16 len;
+ __le16 id;
+};
+
+#define WSM_TX_SEQ_MAX (7)
+#define WSM_TX_SEQ(seq) \
+ ((seq & WSM_TX_SEQ_MAX) << 13)
+#define WSM_TX_LINK_ID_MAX (0x0F)
+#define WSM_TX_LINK_ID(link_id) \
+ ((link_id & WSM_TX_LINK_ID_MAX) << 6)
+
+#define MAX_BEACON_SKIP_TIME_MS 1000
+
+#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 3 / 2)
+
+/* ******************************************************************** */
+/* WSM capability */
+
+#define WSM_STARTUP_IND_ID 0x0801
+
+struct wsm_startup_ind {
+ u16 input_buffers;
+ u16 input_buffer_size;
+ u16 status;
+ u16 hw_id;
+ u16 hw_subid;
+ u16 fw_cap;
+ u16 fw_type;
+ u16 fw_api;
+ u16 fw_build;
+ u16 fw_ver;
+ char fw_label[128];
+ u32 config[4];
+};
+
+/* ******************************************************************** */
+/* WSM commands */
+
+/* 3.1 */
+#define WSM_CONFIGURATION_REQ_ID 0x0009
+#define WSM_CONFIGURATION_RESP_ID 0x0409
+
+struct wsm_tx_power_range {
+ int min_power_level;
+ int max_power_level;
+ u32 stepping;
+};
+
+struct wsm_configuration {
+ /* [in] */ u32 dot11MaxTransmitMsduLifeTime;
+ /* [in] */ u32 dot11MaxReceiveLifeTime;
+ /* [in] */ u32 dot11RtsThreshold;
+ /* [in, out] */ u8 *dot11StationId;
+ /* [in] */ const void *dpdData;
+ /* [in] */ size_t dpdData_size;
+ /* [out] */ u8 dot11FrequencyBandsSupported;
+ /* [out] */ u32 supportedRateMask;
+ /* [out] */ struct wsm_tx_power_range txPowerRange[2];
+};
+
+int wsm_configuration(struct cw1200_common *priv,
+ struct wsm_configuration *arg);
+
+/* 3.3 */
+#define WSM_RESET_REQ_ID 0x000A
+#define WSM_RESET_RESP_ID 0x040A
+struct wsm_reset {
+ /* [in] */ int link_id;
+ /* [in] */ bool reset_statistics;
+};
+
+int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg);
+
+/* 3.5 */
+#define WSM_READ_MIB_REQ_ID 0x0005
+#define WSM_READ_MIB_RESP_ID 0x0405
+int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *buf,
+ size_t buf_size);
+
+/* 3.7 */
+#define WSM_WRITE_MIB_REQ_ID 0x0006
+#define WSM_WRITE_MIB_RESP_ID 0x0406
+int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *buf,
+ size_t buf_size);
+
+/* 3.9 */
+#define WSM_START_SCAN_REQ_ID 0x0007
+#define WSM_START_SCAN_RESP_ID 0x0407
+
+struct wsm_ssid {
+ u8 ssid[32];
+ u32 length;
+};
+
+struct wsm_scan_ch {
+ u16 number;
+ u32 min_chan_time;
+ u32 max_chan_time;
+ u32 tx_power_level;
+};
+
+struct wsm_scan {
+ /* WSM_PHY_BAND_... */
+ u8 band;
+
+ /* WSM_SCAN_TYPE_... */
+ u8 type;
+
+ /* WSM_SCAN_FLAG_... */
+ u8 flags;
+
+ /* WSM_TRANSMIT_RATE_... */
+ u8 max_tx_rate;
+
+ /* Interval period in TUs that the device shall the re- */
+ /* execute the requested scan. Max value supported by the device */
+ /* is 256s. */
+ u32 auto_scan_interval;
+
+ /* Number of probe requests (per SSID) sent to one (1) */
+ /* channel. Zero (0) means that none is send, which */
+ /* means that a passive scan is to be done. Value */
+ /* greater than zero (0) means that an active scan is to */
+ /* be done. */
+ u32 num_probes;
+
+ /* Number of channels to be scanned. */
+ /* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */
+ u8 num_channels;
+
+ /* Number of SSID provided in the scan command (this */
+ /* is zero (0) in broadcast scan) */
+ /* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */
+ u8 num_ssids;
+
+ /* The delay time (in microseconds) period */
+ /* before sending a probe-request. */
+ u8 probe_delay;
+
+ /* SSIDs to be scanned [numOfSSIDs]; */
+ struct wsm_ssid *ssids;
+
+ /* Channels to be scanned [numOfChannels]; */
+ struct wsm_scan_ch *ch;
+};
+
+int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg);
+
+/* 3.11 */
+#define WSM_STOP_SCAN_REQ_ID 0x0008
+#define WSM_STOP_SCAN_RESP_ID 0x0408
+int wsm_stop_scan(struct cw1200_common *priv);
+
+/* 3.13 */
+#define WSM_SCAN_COMPLETE_IND_ID 0x0806
+struct wsm_scan_complete {
+ /* WSM_STATUS_... */
+ u32 status;
+
+ /* WSM_PSM_... */
+ u8 psm;
+
+ /* Number of channels that the scan operation completed. */
+ u8 num_channels;
+};
+
+/* 3.14 */
+#define WSM_TX_CONFIRM_IND_ID 0x0404
+#define WSM_MULTI_TX_CONFIRM_ID 0x041E
+
+struct wsm_tx_confirm {
+ /* Packet identifier used in wsm_tx. */
+ u32 packet_id;
+
+ /* WSM_STATUS_... */
+ u32 status;
+
+ /* WSM_TRANSMIT_RATE_... */
+ u8 tx_rate;
+
+ /* The number of times the frame was transmitted */
+ /* without receiving an acknowledgement. */
+ u8 ack_failures;
+
+ /* WSM_TX_STATUS_... */
+ u16 flags;
+
+ /* The total time in microseconds that the frame spent in */
+ /* the WLAN device before transmission as completed. */
+ u32 media_delay;
+
+ /* The total time in microseconds that the frame spent in */
+ /* the WLAN device before transmission was started. */
+ u32 tx_queue_delay;
+};
+
+/* 3.15 */
+typedef void (*wsm_tx_confirm_cb) (struct cw1200_common *priv,
+ struct wsm_tx_confirm *arg);
+
+/* Note that ideology of wsm_tx struct is different against the rest of
+ * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input
+ * argument for WSM call, but a prepared bytestream to be sent to firmware.
+ * It is filled partly in cw1200_tx, partly in low-level WSM code.
+ * Please pay attention once again: ideology is different.
+ *
+ * Legend:
+ * - [in]: cw1200_tx must fill this field.
+ * - [wsm]: the field is filled by low-level WSM.
+ */
+struct wsm_tx {
+ /* common WSM header */
+ struct wsm_hdr hdr;
+
+ /* Packet identifier that meant to be used in completion. */
+ __le32 packet_id;
+
+ /* WSM_TRANSMIT_RATE_... */
+ u8 max_tx_rate;
+
+ /* WSM_QUEUE_... */
+ u8 queue_id;
+
+ /* True: another packet is pending on the host for transmission. */
+ u8 more;
+
+ /* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */
+ /* Bit 0 = 1 - Start expiry time from receipt of Tx Request */
+ /* Bits 3:1 - PTA Priority */
+ /* Bits 6:4 - Tx Rate Retry Policy */
+ /* Bit 7 - Reserved */
+ u8 flags;
+
+ /* Should be 0. */
+ __le32 reserved;
+
+ /* The elapsed time in TUs, after the initial transmission */
+ /* of an MSDU, after which further attempts to transmit */
+ /* the MSDU shall be terminated. Overrides the global */
+ /* dot11MaxTransmitMsduLifeTime setting [optional] */
+ /* Device will set the default value if this is 0. */
+ __le32 expire_time;
+
+ /* WSM_HT_TX_... */
+ __le32 ht_tx_parameters;
+};
+
+/* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */
+#define WSM_TX_EXTRA_HEADROOM (28)
+
+/* 3.16 */
+#define WSM_RECEIVE_IND_ID 0x0804
+
+struct wsm_rx {
+ /* WSM_STATUS_... */
+ __le32 status;
+
+ /* Specifies the channel of the received packet. */
+ __le16 channel_number;
+
+ /* WSM_TRANSMIT_RATE_... */
+ u8 rx_rate;
+
+ /* This value is expressed in signed Q8.0 format for */
+ /* RSSI and unsigned Q7.1 format for RCPI. */
+ u8 rcpi_rssi;
+
+ /* WSM_RX_STATUS_... */
+ __le32 flags;
+
+ /* Payload */
+ u8 data[0];
+} __packed;
+
+/* = sizeof(generic hi hdr) + sizeof(wsm hdr) */
+#define WSM_RX_EXTRA_HEADROOM (16)
+
+typedef void (*wsm_rx_cb) (struct cw1200_common *priv, struct wsm_rx *arg,
+ struct sk_buff **skb_p);
+
+/* 3.17 */
+struct wsm_event {
+ /* WSM_STATUS_... */
+ /* [out] */ u32 id;
+
+ /* Indication parameters. */
+ /* For error indication, this shall be a 32-bit WSM status. */
+ /* For RCPI or RSSI indication, this should be an 8-bit */
+ /* RCPI or RSSI value. */
+ /* [out] */ u32 data;
+};
+
+struct cw1200_wsm_event {
+ struct list_head link;
+ struct wsm_event evt;
+};
+
+/* 3.18 - 3.22 */
+/* Measurement. Skipped for now. Irrelevent. */
+
+typedef void (*wsm_event_cb) (struct cw1200_common *priv,
+ struct wsm_event *arg);
+
+/* 3.23 */
+#define WSM_JOIN_REQ_ID 0x000B
+#define WSM_JOIN_RESP_ID 0x040B
+
+struct wsm_join {
+ /* WSM_JOIN_MODE_... */
+ u8 mode;
+
+ /* WSM_PHY_BAND_... */
+ u8 band;
+
+ /* Specifies the channel number to join. The channel */
+ /* number will be mapped to an actual frequency */
+ /* according to the band */
+ u16 channel_number;
+
+ /* Specifies the BSSID of the BSS or IBSS to be joined */
+ /* or the IBSS to be started. */
+ u8 bssid[6];
+
+ /* ATIM window of IBSS */
+ /* When ATIM window is zero the initiated IBSS does */
+ /* not support power saving. */
+ u16 atim_window;
+
+ /* WSM_JOIN_PREAMBLE_... */
+ u8 preamble_type;
+
+ /* Specifies if a probe request should be send with the */
+ /* specified SSID when joining to the network. */
+ u8 probe_for_join;
+
+ /* DTIM Period (In multiples of beacon interval) */
+ u8 dtim_period;
+
+ /* WSM_JOIN_FLAGS_... */
+ u8 flags;
+
+ /* Length of the SSID */
+ u32 ssid_len;
+
+ /* Specifies the SSID of the IBSS to join or start */
+ u8 ssid[32];
+
+ /* Specifies the time between TBTTs in TUs */
+ u32 beacon_interval;
+
+ /* A bit mask that defines the BSS basic rate set. */
+ u32 basic_rate_set;
+};
+
+struct wsm_join_cnf {
+ u32 status;
+
+ /* Minimum transmission power level in units of 0.1dBm */
+ u32 min_power_level;
+
+ /* Maximum transmission power level in units of 0.1dBm */
+ u32 max_power_level;
+};
+
+int wsm_join(struct cw1200_common *priv, struct wsm_join *arg);
+
+/* 3.24 */
+struct wsm_join_complete {
+ /* WSM_STATUS_... */
+ u32 status;
+};
+
+/* 3.25 */
+#define WSM_SET_PM_REQ_ID 0x0010
+#define WSM_SET_PM_RESP_ID 0x0410
+struct wsm_set_pm {
+ /* WSM_PSM_... */
+ u8 mode;
+
+ /* in unit of 500us; 0 to use default */
+ u8 fast_psm_idle_period;
+
+ /* in unit of 500us; 0 to use default */
+ u8 ap_psm_change_period;
+
+ /* in unit of 500us; 0 to disable auto-pspoll */
+ u8 min_auto_pspoll_period;
+};
+
+int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg);
+
+/* 3.27 */
+struct wsm_set_pm_complete {
+ u8 psm; /* WSM_PSM_... */
+};
+
+/* 3.28 */
+#define WSM_SET_BSS_PARAMS_REQ_ID 0x0011
+#define WSM_SET_BSS_PARAMS_RESP_ID 0x0411
+struct wsm_set_bss_params {
+ /* This resets the beacon loss counters only */
+ u8 reset_beacon_loss;
+
+ /* The number of lost consecutive beacons after which */
+ /* the WLAN device should indicate the BSS-Lost event */
+ /* to the WLAN host driver. */
+ u8 beacon_lost_count;
+
+ /* The AID received during the association process. */
+ u16 aid;
+
+ /* The operational rate set mask */
+ u32 operational_rate_set;
+};
+
+int wsm_set_bss_params(struct cw1200_common *priv,
+ const struct wsm_set_bss_params *arg);
+
+/* 3.30 */
+#define WSM_ADD_KEY_REQ_ID 0x000C
+#define WSM_ADD_KEY_RESP_ID 0x040C
+struct wsm_add_key {
+ u8 type; /* WSM_KEY_TYPE_... */
+ u8 index; /* Key entry index: 0 -- WSM_KEY_MAX_INDEX */
+ u16 reserved;
+ union {
+ struct {
+ u8 peer[6]; /* MAC address of the peer station */
+ u8 reserved;
+ u8 keylen; /* Key length in bytes */
+ u8 keydata[16]; /* Key data */
+ } __packed wep_pairwise;
+ struct {
+ u8 keyid; /* Unique per key identifier (0..3) */
+ u8 keylen; /* Key length in bytes */
+ u16 reserved;
+ u8 keydata[16]; /* Key data */
+ } __packed wep_group;
+ struct {
+ u8 peer[6]; /* MAC address of the peer station */
+ u16 reserved;
+ u8 keydata[16]; /* TKIP key data */
+ u8 rx_mic_key[8]; /* Rx MIC key */
+ u8 tx_mic_key[8]; /* Tx MIC key */
+ } __packed tkip_pairwise;
+ struct {
+ u8 keydata[16]; /* TKIP key data */
+ u8 rx_mic_key[8]; /* Rx MIC key */
+ u8 keyid; /* Key ID */
+ u8 reserved[3];
+ u8 rx_seqnum[8]; /* Receive Sequence Counter */
+ } __packed tkip_group;
+ struct {
+ u8 peer[6]; /* MAC address of the peer station */
+ u16 reserved;
+ u8 keydata[16]; /* AES key data */
+ } __packed aes_pairwise;
+ struct {
+ u8 keydata[16]; /* AES key data */
+ u8 keyid; /* Key ID */
+ u8 reserved[3];
+ u8 rx_seqnum[8]; /* Receive Sequence Counter */
+ } __packed aes_group;
+ struct {
+ u8 peer[6]; /* MAC address of the peer station */
+ u8 keyid; /* Key ID */
+ u8 reserved;
+ u8 keydata[16]; /* WAPI key data */
+ u8 mic_key[16]; /* MIC key data */
+ } __packed wapi_pairwise;
+ struct {
+ u8 keydata[16]; /* WAPI key data */
+ u8 mic_key[16]; /* MIC key data */
+ u8 keyid; /* Key ID */
+ u8 reserved[3];
+ } __packed wapi_group;
+ } __packed;
+} __packed;
+
+int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg);
+
+/* 3.32 */
+#define WSM_REMOVE_KEY_REQ_ID 0x000D
+#define WSM_REMOVE_KEY_RESP_ID 0x040D
+struct wsm_remove_key {
+ u8 index; /* Key entry index : 0-10 */
+};
+
+int wsm_remove_key(struct cw1200_common *priv,
+ const struct wsm_remove_key *arg);
+
+/* 3.34 */
+struct wsm_set_tx_queue_params {
+ /* WSM_ACK_POLICY_... */
+ u8 ackPolicy;
+
+ /* Medium Time of TSPEC (in 32us units) allowed per */
+ /* One Second Averaging Period for this queue. */
+ u16 allowedMediumTime;
+
+ /* dot11MaxTransmitMsduLifetime to be used for the */
+ /* specified queue. */
+ u32 maxTransmitLifetime;
+};
+
+struct wsm_tx_queue_params {
+ /* NOTE: index is a linux queue id. */
+ struct wsm_set_tx_queue_params params[4];
+};
+
+
+#define WSM_TX_QUEUE_SET(queue_params, queue, ack_policy, allowed_time,\
+ max_life_time) \
+do { \
+ struct wsm_set_tx_queue_params *p = &(queue_params)->params[queue]; \
+ p->ackPolicy = (ack_policy); \
+ p->allowedMediumTime = (allowed_time); \
+ p->maxTransmitLifetime = (max_life_time); \
+} while (0)
+
+int wsm_set_tx_queue_params(struct cw1200_common *priv,
+ const struct wsm_set_tx_queue_params *arg, u8 id);
+
+/* 3.36 */
+#define WSM_EDCA_PARAMS_REQ_ID 0x0013
+#define WSM_EDCA_PARAMS_RESP_ID 0x0413
+struct wsm_edca_queue_params {
+ /* CWmin (in slots) for the access class. */
+ __le16 cwmin;
+
+ /* CWmax (in slots) for the access class. */
+ __le16 cwmax;
+
+ /* AIFS (in slots) for the access class. */
+ __le16 aifns;
+
+ /* TX OP Limit (in microseconds) for the access class. */
+ __le16 txop_limit;
+
+ /* dot11MaxReceiveLifetime to be used for the specified */
+ /* the access class. Overrides the global */
+ /* dot11MaxReceiveLifetime value */
+ __le32 max_rx_lifetime;
+} __packed;
+
+struct wsm_edca_params {
+ /* NOTE: index is a linux queue id. */
+ struct wsm_edca_queue_params params[4];
+ bool uapsd_enable[4];
+};
+
+#define TXOP_UNIT 32
+#define WSM_EDCA_SET(__edca, __queue, __aifs, __cw_min, __cw_max, __txop, __lifetime,\
+ __uapsd) \
+ do { \
+ struct wsm_edca_queue_params *p = &(__edca)->params[__queue]; \
+ p->cwmin = (__cw_min); \
+ p->cwmax = (__cw_max); \
+ p->aifns = (__aifs); \
+ p->txop_limit = ((__txop) * TXOP_UNIT); \
+ p->max_rx_lifetime = (__lifetime); \
+ (__edca)->uapsd_enable[__queue] = (__uapsd); \
+ } while (0)
+
+int wsm_set_edca_params(struct cw1200_common *priv,
+ const struct wsm_edca_params *arg);
+
+int wsm_set_uapsd_param(struct cw1200_common *priv,
+ const struct wsm_edca_params *arg);
+
+/* 3.38 */
+/* Set-System info. Skipped for now. Irrelevent. */
+
+/* 3.40 */
+#define WSM_SWITCH_CHANNEL_REQ_ID 0x0016
+#define WSM_SWITCH_CHANNEL_RESP_ID 0x0416
+
+struct wsm_switch_channel {
+ /* 1 - means the STA shall not transmit any further */
+ /* frames until the channel switch has completed */
+ u8 mode;
+
+ /* Number of TBTTs until channel switch occurs. */
+ /* 0 - indicates switch shall occur at any time */
+ /* 1 - occurs immediately before the next TBTT */
+ u8 switch_count;
+
+ /* The new channel number to switch to. */
+ /* Note this is defined as per section 2.7. */
+ u16 channel_number;
+};
+
+int wsm_switch_channel(struct cw1200_common *priv,
+ const struct wsm_switch_channel *arg);
+
+typedef void (*wsm_channel_switch_cb) (struct cw1200_common *priv);
+
+#define WSM_START_REQ_ID 0x0017
+#define WSM_START_RESP_ID 0x0417
+
+struct wsm_start {
+ /* WSM_START_MODE_... */
+ /* [in] */ u8 mode;
+
+ /* WSM_PHY_BAND_... */
+ /* [in] */ u8 band;
+
+ /* Channel number */
+ /* [in] */ u16 channel_number;
+
+ /* Client Traffic window in units of TU */
+ /* Valid only when mode == ..._P2P */
+ /* [in] */ u32 ct_window;
+
+ /* Interval between two consecutive */
+ /* beacon transmissions in TU. */
+ /* [in] */ u32 beacon_interval;
+
+ /* DTIM period in terms of beacon intervals */
+ /* [in] */ u8 dtim_period;
+
+ /* WSM_JOIN_PREAMBLE_... */
+ /* [in] */ u8 preamble;
+
+ /* The delay time (in microseconds) period */
+ /* before sending a probe-request. */
+ /* [in] */ u8 probe_delay;
+
+ /* Length of the SSID */
+ /* [in] */ u8 ssid_len;
+
+ /* SSID of the BSS or P2P_GO to be started now. */
+ /* [in] */ u8 ssid[32];
+
+ /* The basic supported rates for the MiniAP. */
+ /* [in] */ u32 basic_rate_set;
+};
+
+int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg);
+
+#define WSM_BEACON_TRANSMIT_REQ_ID 0x0018
+#define WSM_BEACON_TRANSMIT_RESP_ID 0x0418
+
+struct wsm_beacon_transmit {
+ /* 1: enable; 0: disable */
+ /* [in] */ u8 enable_beaconing;
+};
+
+int wsm_beacon_transmit(struct cw1200_common *priv,
+ const struct wsm_beacon_transmit *arg);
+
+int wsm_start_find(struct cw1200_common *priv);
+
+int wsm_stop_find(struct cw1200_common *priv);
+
+typedef void (*wsm_find_complete_cb) (struct cw1200_common *priv, u32 status);
+
+struct wsm_suspend_resume {
+ /* See 3.52 */
+ /* Link ID */
+ /* [out] */ int link_id;
+ /* Stop sending further Tx requests down to device for this link */
+ /* [out] */ bool stop;
+ /* Transmit multicast Frames */
+ /* [out] */ bool multicast;
+ /* The AC on which Tx to be suspended /resumed. */
+ /* This is applicable only for U-APSD */
+ /* WSM_QUEUE_... */
+ /* [out] */ int queue;
+};
+
+typedef void (*wsm_suspend_resume_cb) (struct cw1200_common *priv,
+ struct wsm_suspend_resume *arg);
+
+/* 3.54 Update-IE request. */
+struct wsm_update_ie {
+ /* WSM_UPDATE_IE_... */
+ /* [in] */ u16 what;
+ /* [in] */ u16 count;
+ /* [in] */ u8 *ies;
+ /* [in] */ size_t length;
+};
+
+int wsm_update_ie(struct cw1200_common *priv,
+ const struct wsm_update_ie *arg);
+
+/* 3.56 */
+struct wsm_map_link {
+ /* MAC address of the remote device */
+ /* [in] */ u8 mac_addr[6];
+ /* [in] */ u8 link_id;
+};
+
+int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg);
+
+/* ******************************************************************** */
+/* MIB shortcats */
+
+static inline int wsm_set_output_power(struct cw1200_common *priv,
+ int power_level)
+{
+ __le32 val = __cpu_to_le32(power_level);
+ return wsm_write_mib(priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL,
+ &val, sizeof(val));
+}
+
+static inline int wsm_set_beacon_wakeup_period(struct cw1200_common *priv,
+ unsigned dtim_interval,
+ unsigned listen_interval)
+{
+ struct {
+ u8 numBeaconPeriods;
+ u8 reserved;
+ __le16 listenInterval;
+ } val = {
+ dtim_interval, 0, __cpu_to_le16(listen_interval)
+ };
+
+ if (dtim_interval > 0xFF || listen_interval > 0xFFFF)
+ return -EINVAL;
+ else
+ return wsm_write_mib(priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD,
+ &val, sizeof(val));
+}
+
+struct wsm_rcpi_rssi_threshold {
+ u8 rssiRcpiMode; /* WSM_RCPI_RSSI_... */
+ u8 lowerThreshold;
+ u8 upperThreshold;
+ u8 rollingAverageCount;
+};
+
+static inline int wsm_set_rcpi_rssi_threshold(struct cw1200_common *priv,
+ struct wsm_rcpi_rssi_threshold *arg)
+{
+ return wsm_write_mib(priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg,
+ sizeof(*arg));
+}
+
+struct wsm_mib_counters_table {
+ __le32 plcp_errors;
+ __le32 fcs_errors;
+ __le32 tx_packets;
+ __le32 rx_packets;
+ __le32 rx_packet_errors;
+ __le32 rx_decryption_failures;
+ __le32 rx_mic_failures;
+ __le32 rx_no_key_failures;
+ __le32 tx_multicast_frames;
+ __le32 tx_frames_success;
+ __le32 tx_frame_failures;
+ __le32 tx_frames_retried;
+ __le32 tx_frames_multi_retried;
+ __le32 rx_frame_duplicates;
+ __le32 rts_success;
+ __le32 rts_failures;
+ __le32 ack_failures;
+ __le32 rx_multicast_frames;
+ __le32 rx_frames_success;
+ __le32 rx_cmac_icv_errors;
+ __le32 rx_cmac_replays;
+ __le32 rx_mgmt_ccmp_replays;
+} __packed;
+
+static inline int wsm_get_counters_table(struct cw1200_common *priv,
+ struct wsm_mib_counters_table *arg)
+{
+ return wsm_read_mib(priv, WSM_MIB_ID_COUNTERS_TABLE,
+ arg, sizeof(*arg));
+}
+
+static inline int wsm_get_station_id(struct cw1200_common *priv, u8 *mac)
+{
+ return wsm_read_mib(priv, WSM_MIB_ID_DOT11_STATION_ID, mac, ETH_ALEN);
+}
+
+struct wsm_rx_filter {
+ bool promiscuous;
+ bool bssid;
+ bool fcs;
+ bool probeResponder;
+};
+
+static inline int wsm_set_rx_filter(struct cw1200_common *priv,
+ const struct wsm_rx_filter *arg)
+{
+ __le32 val = 0;
+ if (arg->promiscuous)
+ val |= __cpu_to_le32(BIT(0));
+ if (arg->bssid)
+ val |= __cpu_to_le32(BIT(1));
+ if (arg->fcs)
+ val |= __cpu_to_le32(BIT(2));
+ if (arg->probeResponder)
+ val |= __cpu_to_le32(BIT(3));
+ return wsm_write_mib(priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val));
+}
+
+int wsm_set_probe_responder(struct cw1200_common *priv, bool enable);
+
+#define WSM_BEACON_FILTER_IE_HAS_CHANGED BIT(0)
+#define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT BIT(1)
+#define WSM_BEACON_FILTER_IE_HAS_APPEARED BIT(2)
+
+struct wsm_beacon_filter_table_entry {
+ u8 ie_id;
+ u8 flags;
+ u8 oui[3];
+ u8 match_data[3];
+} __packed;
+
+struct wsm_mib_beacon_filter_table {
+ __le32 num;
+ struct wsm_beacon_filter_table_entry entry[10];
+} __packed;
+
+static inline int wsm_set_beacon_filter_table(struct cw1200_common *priv,
+ struct wsm_mib_beacon_filter_table *ft)
+{
+ size_t size = __le32_to_cpu(ft->num) *
+ sizeof(struct wsm_beacon_filter_table_entry) +
+ sizeof(__le32);
+
+ return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size);
+}
+
+#define WSM_BEACON_FILTER_ENABLE BIT(0) /* Enable/disable beacon filtering */
+#define WSM_BEACON_FILTER_AUTO_ERP BIT(1) /* If 1 FW will handle ERP IE changes internally */
+
+struct wsm_beacon_filter_control {
+ int enabled;
+ int bcn_count;
+};
+
+static inline int wsm_beacon_filter_control(struct cw1200_common *priv,
+ struct wsm_beacon_filter_control *arg)
+{
+ struct {
+ __le32 enabled;
+ __le32 bcn_count;
+ } val;
+ val.enabled = __cpu_to_le32(arg->enabled);
+ val.bcn_count = __cpu_to_le32(arg->bcn_count);
+ return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val,
+ sizeof(val));
+}
+
+enum wsm_power_mode {
+ wsm_power_mode_active = 0,
+ wsm_power_mode_doze = 1,
+ wsm_power_mode_quiescent = 2,
+};
+
+struct wsm_operational_mode {
+ enum wsm_power_mode power_mode;
+ int disable_more_flag_usage;
+ int perform_ant_diversity;
+};
+
+static inline int wsm_set_operational_mode(struct cw1200_common *priv,
+ const struct wsm_operational_mode *arg)
+{
+ u8 val = arg->power_mode;
+ if (arg->disable_more_flag_usage)
+ val |= BIT(4);
+ if (arg->perform_ant_diversity)
+ val |= BIT(5);
+ return wsm_write_mib(priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val,
+ sizeof(val));
+}
+
+struct wsm_template_frame {
+ u8 frame_type;
+ u8 rate;
+ struct sk_buff *skb;
+};
+
+static inline int wsm_set_template_frame(struct cw1200_common *priv,
+ struct wsm_template_frame *arg)
+{
+ int ret;
+ u8 *p = skb_push(arg->skb, 4);
+ p[0] = arg->frame_type;
+ p[1] = arg->rate;
+ ((u16 *)p)[1] = __cpu_to_le16(arg->skb->len - 4);
+ ret = wsm_write_mib(priv, WSM_MIB_ID_TEMPLATE_FRAME, p, arg->skb->len);
+ skb_pull(arg->skb, 4);
+ return ret;
+}
+
+
+struct wsm_protected_mgmt_policy {
+ bool protectedMgmtEnable;
+ bool unprotectedMgmtFramesAllowed;
+ bool encryptionForAuthFrame;
+};
+
+static inline int wsm_set_protected_mgmt_policy(struct cw1200_common *priv,
+ struct wsm_protected_mgmt_policy *arg)
+{
+ __le32 val = 0;
+ int ret;
+ if (arg->protectedMgmtEnable)
+ val |= __cpu_to_le32(BIT(0));
+ if (arg->unprotectedMgmtFramesAllowed)
+ val |= __cpu_to_le32(BIT(1));
+ if (arg->encryptionForAuthFrame)
+ val |= __cpu_to_le32(BIT(2));
+ ret = wsm_write_mib(priv, WSM_MIB_ID_PROTECTED_MGMT_POLICY,
+ &val, sizeof(val));
+ return ret;
+}
+
+struct wsm_mib_block_ack_policy {
+ u8 tx_tid;
+ u8 reserved1;
+ u8 rx_tid;
+ u8 reserved2;
+} __packed;
+
+static inline int wsm_set_block_ack_policy(struct cw1200_common *priv,
+ u8 tx_tid_policy,
+ u8 rx_tid_policy)
+{
+ struct wsm_mib_block_ack_policy val = {
+ .tx_tid = tx_tid_policy,
+ .rx_tid = rx_tid_policy,
+ };
+ return wsm_write_mib(priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val,
+ sizeof(val));
+}
+
+struct wsm_mib_association_mode {
+ u8 flags; /* WSM_ASSOCIATION_MODE_... */
+ u8 preamble; /* WSM_JOIN_PREAMBLE_... */
+ u8 greenfield; /* 1 for greenfield */
+ u8 mpdu_start_spacing;
+ __le32 basic_rate_set;
+} __packed;
+
+static inline int wsm_set_association_mode(struct cw1200_common *priv,
+ struct wsm_mib_association_mode *arg)
+{
+ return wsm_write_mib(priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg,
+ sizeof(*arg));
+}
+
+#define WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED BIT(2)
+#define WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT BIT(3)
+struct wsm_tx_rate_retry_policy {
+ u8 index;
+ u8 short_retries;
+ u8 long_retries;
+ /* BIT(2) - Terminate retries when Tx rate retry policy
+ * finishes.
+ * BIT(3) - Count initial frame transmission as part of
+ * rate retry counting but not as a retry
+ * attempt
+ */
+ u8 flags;
+ u8 rate_recoveries;
+ u8 reserved[3];
+ __le32 rate_count_indices[3];
+} __packed;
+
+struct wsm_set_tx_rate_retry_policy {
+ u8 num;
+ u8 reserved[3];
+ struct wsm_tx_rate_retry_policy tbl[8];
+} __packed;
+
+static inline int wsm_set_tx_rate_retry_policy(struct cw1200_common *priv,
+ struct wsm_set_tx_rate_retry_policy *arg)
+{
+ size_t size = 4 + arg->num * sizeof(struct wsm_tx_rate_retry_policy);
+ return wsm_write_mib(priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg,
+ size);
+}
+
+/* 4.32 SetEtherTypeDataFrameFilter */
+struct wsm_ether_type_filter_hdr {
+ u8 num; /* Up to WSM_MAX_FILTER_ELEMENTS */
+ u8 reserved[3];
+} __packed;
+
+struct wsm_ether_type_filter {
+ u8 action; /* WSM_FILTER_ACTION_XXX */
+ u8 reserved;
+ __le16 type; /* Type of ethernet frame */
+} __packed;
+
+static inline int wsm_set_ether_type_filter(struct cw1200_common *priv,
+ struct wsm_ether_type_filter_hdr *arg)
+{
+ size_t size = sizeof(struct wsm_ether_type_filter_hdr) +
+ arg->num * sizeof(struct wsm_ether_type_filter);
+ return wsm_write_mib(priv, WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER,
+ arg, size);
+}
+
+/* 4.33 SetUDPPortDataFrameFilter */
+struct wsm_udp_port_filter_hdr {
+ u8 num; /* Up to WSM_MAX_FILTER_ELEMENTS */
+ u8 reserved[3];
+} __packed;
+
+struct wsm_udp_port_filter {
+ u8 action; /* WSM_FILTER_ACTION_XXX */
+ u8 type; /* WSM_FILTER_PORT_TYPE_XXX */
+ __le16 port; /* Port number */
+} __packed;
+
+static inline int wsm_set_udp_port_filter(struct cw1200_common *priv,
+ struct wsm_udp_port_filter_hdr *arg)
+{
+ size_t size = sizeof(struct wsm_udp_port_filter_hdr) +
+ arg->num * sizeof(struct wsm_udp_port_filter);
+ return wsm_write_mib(priv, WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER,
+ arg, size);
+}
+
+/* Undocumented MIBs: */
+/* 4.35 P2PDeviceInfo */
+#define D11_MAX_SSID_LEN (32)
+
+struct wsm_p2p_device_type {
+ __le16 category_id;
+ u8 oui[4];
+ __le16 subcategory_id;
+} __packed;
+
+struct wsm_p2p_device_info {
+ struct wsm_p2p_device_type primaryDevice;
+ u8 reserved1[3];
+ u8 devname_size;
+ u8 local_devname[D11_MAX_SSID_LEN];
+ u8 reserved2[3];
+ u8 num_secdev_supported;
+ struct wsm_p2p_device_type secdevs[0];
+} __packed;
+
+/* 4.36 SetWCDMABand - WO */
+struct wsm_cdma_band {
+ u8 wcdma_band;
+ u8 reserved[3];
+} __packed;
+
+/* 4.37 GroupTxSequenceCounter - RO */
+struct wsm_group_tx_seq {
+ __le32 bits_47_16;
+ __le16 bits_15_00;
+ __le16 reserved;
+} __packed;
+
+/* 4.39 SetHtProtection - WO */
+#define WSM_DUAL_CTS_PROT_ENB (1 << 0)
+#define WSM_NON_GREENFIELD_STA_PRESENT (1 << 1)
+#define WSM_HT_PROT_MODE__NO_PROT (0 << 2)
+#define WSM_HT_PROT_MODE__NON_MEMBER (1 << 2)
+#define WSM_HT_PROT_MODE__20_MHZ (2 << 2)
+#define WSM_HT_PROT_MODE__NON_HT_MIXED (3 << 2)
+#define WSM_LSIG_TXOP_PROT_FULL (1 << 4)
+#define WSM_LARGE_L_LENGTH_PROT (1 << 5)
+
+struct wsm_ht_protection {
+ __le32 flags;
+} __packed;
+
+/* 4.40 GPIO Command - R/W */
+#define WSM_GPIO_COMMAND_SETUP 0
+#define WSM_GPIO_COMMAND_READ 1
+#define WSM_GPIO_COMMAND_WRITE 2
+#define WSM_GPIO_COMMAND_RESET 3
+#define WSM_GPIO_ALL_PINS 0xFF
+
+struct wsm_gpio_command {
+ u8 command;
+ u8 pin;
+ __le16 config;
+} __packed;
+
+/* 4.41 TSFCounter - RO */
+struct wsm_tsf_counter {
+ __le64 tsf_counter;
+} __packed;
+
+/* 4.43 Keep alive period */
+struct wsm_keep_alive_period {
+ __le16 period;
+ u8 reserved[2];
+} __packed;
+
+static inline int wsm_keep_alive_period(struct cw1200_common *priv,
+ int period)
+{
+ struct wsm_keep_alive_period arg = {
+ .period = __cpu_to_le16(period),
+ };
+ return wsm_write_mib(priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD,
+ &arg, sizeof(arg));
+};
+
+/* BSSID filtering */
+struct wsm_set_bssid_filtering {
+ u8 filter;
+ u8 reserved[3];
+} __packed;
+
+static inline int wsm_set_bssid_filtering(struct cw1200_common *priv,
+ bool enabled)
+{
+ struct wsm_set_bssid_filtering arg = {
+ .filter = !enabled,
+ };
+ return wsm_write_mib(priv, WSM_MIB_ID_DISABLE_BSSID_FILTER,
+ &arg, sizeof(arg));
+}
+
+/* Multicast filtering - 4.5 */
+struct wsm_mib_multicast_filter {
+ __le32 enable;
+ __le32 num_addrs;
+ u8 macaddrs[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN];
+} __packed;
+
+static inline int wsm_set_multicast_filter(struct cw1200_common *priv,
+ struct wsm_mib_multicast_filter *fp)
+{
+ return wsm_write_mib(priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE,
+ fp, sizeof(*fp));
+}
+
+/* ARP IPv4 filtering - 4.10 */
+struct wsm_mib_arp_ipv4_filter {
+ __le32 enable;
+ __be32 ipv4addrs[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES];
+} __packed;
+
+static inline int wsm_set_arp_ipv4_filter(struct cw1200_common *priv,
+ struct wsm_mib_arp_ipv4_filter *fp)
+{
+ return wsm_write_mib(priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE,
+ fp, sizeof(*fp));
+}
+
+/* P2P Power Save Mode Info - 4.31 */
+struct wsm_p2p_ps_modeinfo {
+ u8 opp_ps_ct_window;
+ u8 count;
+ u8 reserved;
+ u8 dtim_count;
+ __le32 duration;
+ __le32 interval;
+ __le32 start_time;
+} __packed;
+
+static inline int wsm_set_p2p_ps_modeinfo(struct cw1200_common *priv,
+ struct wsm_p2p_ps_modeinfo *mi)
+{
+ return wsm_write_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO,
+ mi, sizeof(*mi));
+}
+
+static inline int wsm_get_p2p_ps_modeinfo(struct cw1200_common *priv,
+ struct wsm_p2p_ps_modeinfo *mi)
+{
+ return wsm_read_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO,
+ mi, sizeof(*mi));
+}
+
+/* UseMultiTxConfMessage */
+
+static inline int wsm_use_multi_tx_conf(struct cw1200_common *priv,
+ bool enabled)
+{
+ __le32 arg = enabled ? __cpu_to_le32(1) : 0;
+
+ return wsm_write_mib(priv, WSM_MIB_USE_MULTI_TX_CONF,
+ &arg, sizeof(arg));
+}
+
+
+/* 4.26 SetUpasdInformation */
+struct wsm_uapsd_info {
+ __le16 uapsd_flags;
+ __le16 min_auto_trigger_interval;
+ __le16 max_auto_trigger_interval;
+ __le16 auto_trigger_step;
+};
+
+static inline int wsm_set_uapsd_info(struct cw1200_common *priv,
+ struct wsm_uapsd_info *arg)
+{
+ return wsm_write_mib(priv, WSM_MIB_ID_SET_UAPSD_INFORMATION,
+ arg, sizeof(*arg));
+}
+
+/* 4.22 OverrideInternalTxRate */
+struct wsm_override_internal_txrate {
+ u8 internalTxRate;
+ u8 nonErpInternalTxRate;
+ u8 reserved[2];
+} __packed;
+
+static inline int wsm_set_override_internal_txrate(struct cw1200_common *priv,
+ struct wsm_override_internal_txrate *arg)
+{
+ return wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
+ arg, sizeof(*arg));
+}
+
+/* ******************************************************************** */
+/* WSM TX port control */
+
+void wsm_lock_tx(struct cw1200_common *priv);
+void wsm_lock_tx_async(struct cw1200_common *priv);
+bool wsm_flush_tx(struct cw1200_common *priv);
+void wsm_unlock_tx(struct cw1200_common *priv);
+
+/* ******************************************************************** */
+/* WSM / BH API */
+
+int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len);
+int wsm_handle_rx(struct cw1200_common *priv, u16 id, struct wsm_hdr *wsm,
+ struct sk_buff **skb_p);
+
+/* ******************************************************************** */
+/* wsm_buf API */
+
+struct wsm_buf {
+ u8 *begin;
+ u8 *data;
+ u8 *end;
+};
+
+void wsm_buf_init(struct wsm_buf *buf);
+void wsm_buf_deinit(struct wsm_buf *buf);
+
+/* ******************************************************************** */
+/* wsm_cmd API */
+
+struct wsm_cmd {
+ spinlock_t lock; /* Protect structure from multiple access */
+ int done;
+ u8 *ptr;
+ size_t len;
+ void *arg;
+ int ret;
+ u16 cmd;
+};
+
+/* ******************************************************************** */
+/* WSM TX buffer access */
+
+int wsm_get_tx(struct cw1200_common *priv, u8 **data,
+ size_t *tx_len, int *burst);
+void wsm_txed(struct cw1200_common *priv, u8 *data);
+
+/* ******************************************************************** */
+/* Queue mapping: WSM <---> linux */
+/* Linux: VO VI BE BK */
+/* WSM: BE BK VI VO */
+
+static inline u8 wsm_queue_id_to_linux(u8 queue_id)
+{
+ static const u8 queue_mapping[] = {
+ 2, 3, 1, 0
+ };
+ return queue_mapping[queue_id];
+}
+
+static inline u8 wsm_queue_id_to_wsm(u8 queue_id)
+{
+ static const u8 queue_mapping[] = {
+ 3, 2, 0, 1
+ };
+ return queue_mapping[queue_id];
+}
+
+#endif /* CW1200_HWIO_H_INCLUDED */
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c
index 15920aaa5dd6..f8ab193009cd 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/ipw2x00/ipw2100.c
@@ -6242,8 +6242,6 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
if ((val & 0x0000ff00) != 0)
pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
- pci_set_power_state(pci_dev, PCI_D0);
-
if (!ipw2100_hw_is_adapter_in_system(dev)) {
printk(KERN_WARNING DRV_NAME
"Device not found via register read.\n");
diff --git a/drivers/net/wireless/iwlegacy/commands.h b/drivers/net/wireless/iwlegacy/commands.h
index 3b6c99400892..048421511988 100644
--- a/drivers/net/wireless/iwlegacy/commands.h
+++ b/drivers/net/wireless/iwlegacy/commands.h
@@ -1348,14 +1348,6 @@ struct il_rx_mpdu_res_start {
#define TX_CMD_SEC_KEY128 0x08
/*
- * security overhead sizes
- */
-#define WEP_IV_LEN 4
-#define WEP_ICV_LEN 4
-#define CCMP_MIC_LEN 8
-#define TKIP_ICV_LEN 4
-
-/*
* C_TX = 0x1c (command)
*/
diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h
index 48545ab00311..de2c9514bef6 100644
--- a/drivers/net/wireless/iwlwifi/dvm/agn.h
+++ b/drivers/net/wireless/iwlwifi/dvm/agn.h
@@ -76,13 +76,16 @@
#define IWL_INVALID_STATION 255
/* device operations */
-extern struct iwl_lib_ops iwl1000_lib;
-extern struct iwl_lib_ops iwl2000_lib;
-extern struct iwl_lib_ops iwl2030_lib;
-extern struct iwl_lib_ops iwl5000_lib;
-extern struct iwl_lib_ops iwl5150_lib;
-extern struct iwl_lib_ops iwl6000_lib;
-extern struct iwl_lib_ops iwl6030_lib;
+extern const struct iwl_dvm_cfg iwl_dvm_1000_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_2000_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_105_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_2030_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_5000_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_5150_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_6000_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_6005_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_6050_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_6030_cfg;
#define TIME_UNIT 1024
@@ -291,8 +294,8 @@ void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena);
static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv)
{
- return priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist;
+ return priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist;
}
#ifdef CONFIG_IWLWIFI_DEBUG
diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.c b/drivers/net/wireless/iwlwifi/dvm/calib.c
index d6c4cf2ad7c5..1b0f0d502568 100644
--- a/drivers/net/wireless/iwlwifi/dvm/calib.c
+++ b/drivers/net/wireless/iwlwifi/dvm/calib.c
@@ -521,7 +521,7 @@ static int iwl_enhance_sensitivity_write(struct iwl_priv *priv)
iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]);
- if (priv->cfg->base_params->hd_v2) {
+ if (priv->lib->hd_v2) {
cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
HD_INA_NON_SQUARE_DET_OFDM_DATA_V2;
cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
@@ -895,7 +895,7 @@ static void iwlagn_gain_computation(struct iwl_priv *priv,
continue;
}
- delta_g = (priv->cfg->base_params->chain_noise_scale *
+ delta_g = (priv->lib->chain_noise_scale *
((s32)average_noise[default_chain] -
(s32)average_noise[i])) / 1500;
@@ -1051,8 +1051,8 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv)
return;
/* Analyze signal for disconnected antenna */
- if (priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist) {
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist) {
/* Disable disconnected antenna algorithm for advanced
bt coex, assuming valid antennas are connected */
data->active_chains = priv->nvm_data->valid_rx_ant;
diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h
index 95ca026ecc9d..ebdac909f0cd 100644
--- a/drivers/net/wireless/iwlwifi/dvm/commands.h
+++ b/drivers/net/wireless/iwlwifi/dvm/commands.h
@@ -838,10 +838,6 @@ struct iwl_qosparam_cmd {
#define STA_MODIFY_DELBA_TID_MSK 0x10
#define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20
-/* Receiver address (actually, Rx station's index into station table),
- * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */
-#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid))
-
/* agn */
struct iwl_keyinfo {
__le16 key_flags;
@@ -1225,14 +1221,6 @@ struct iwl_rx_mpdu_res_start {
#define TX_CMD_SEC_KEY128 0x08
/*
- * security overhead sizes
- */
-#define WEP_IV_LEN 4
-#define WEP_ICV_LEN 4
-#define CCMP_MIC_LEN 8
-#define TKIP_ICV_LEN 4
-
-/*
* REPLY_TX = 0x1c (command)
*/
diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h
index 71ea77576d22..f1b8df16dbba 100644
--- a/drivers/net/wireless/iwlwifi/dvm/dev.h
+++ b/drivers/net/wireless/iwlwifi/dvm/dev.h
@@ -568,16 +568,61 @@ struct iwl_hw_params {
const struct iwl_sensitivity_ranges *sens;
};
-struct iwl_lib_ops {
- /* set hw dependent parameters */
+/**
+ * struct iwl_dvm_bt_params - DVM specific BT (coex) parameters
+ * @advanced_bt_coexist: support advanced bt coexist
+ * @bt_init_traffic_load: specify initial bt traffic load
+ * @bt_prio_boost: default bt priority boost value
+ * @agg_time_limit: maximum number of uSec in aggregation
+ * @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode
+ */
+struct iwl_dvm_bt_params {
+ bool advanced_bt_coexist;
+ u8 bt_init_traffic_load;
+ u32 bt_prio_boost;
+ u16 agg_time_limit;
+ bool bt_sco_disable;
+ bool bt_session_2;
+};
+
+/**
+ * struct iwl_dvm_cfg - DVM firmware specific device configuration
+ * @set_hw_params: set hardware parameters
+ * @set_channel_switch: send channel switch command
+ * @nic_config: apply device specific configuration
+ * @temperature: read temperature
+ * @adv_thermal_throttle: support advance thermal throttle
+ * @support_ct_kill_exit: support ct kill exit condition
+ * @plcp_delta_threshold: plcp error rate threshold used to trigger
+ * radio tuning when there is a high receiving plcp error rate
+ * @chain_noise_scale: default chain noise scale used for gain computation
+ * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up
+ * @no_idle_support: do not support idle mode
+ * @bt_params: pointer to BT parameters
+ * @need_temp_offset_calib: need to perform temperature offset calibration
+ * @no_xtal_calib: some devices do not need crystal calibration data,
+ * don't send it to those
+ * @temp_offset_v2: support v2 of temperature offset calibration
+ * @adv_pm: advanced power management
+ */
+struct iwl_dvm_cfg {
void (*set_hw_params)(struct iwl_priv *priv);
int (*set_channel_switch)(struct iwl_priv *priv,
struct ieee80211_channel_switch *ch_switch);
- /* device specific configuration */
void (*nic_config)(struct iwl_priv *priv);
-
- /* temperature */
void (*temperature)(struct iwl_priv *priv);
+
+ const struct iwl_dvm_bt_params *bt_params;
+ s32 chain_noise_scale;
+ u8 plcp_delta_threshold;
+ bool adv_thermal_throttle;
+ bool support_ct_kill_exit;
+ bool hd_v2;
+ bool no_idle_support;
+ bool need_temp_offset_calib;
+ bool no_xtal_calib;
+ bool temp_offset_v2;
+ bool adv_pm;
};
struct iwl_wipan_noa_data {
@@ -610,7 +655,7 @@ struct iwl_priv {
struct device *dev; /* for debug prints only */
const struct iwl_cfg *cfg;
const struct iwl_fw *fw;
- const struct iwl_lib_ops *lib;
+ const struct iwl_dvm_cfg *lib;
unsigned long status;
spinlock_t sta_lock;
diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c
index c48907c8ab43..352c6cb7b4f1 100644
--- a/drivers/net/wireless/iwlwifi/dvm/devices.c
+++ b/drivers/net/wireless/iwlwifi/dvm/devices.c
@@ -174,10 +174,13 @@ static void iwl1000_hw_set_hw_params(struct iwl_priv *priv)
priv->hw_params.sens = &iwl1000_sensitivity;
}
-struct iwl_lib_ops iwl1000_lib = {
+const struct iwl_dvm_cfg iwl_dvm_1000_cfg = {
.set_hw_params = iwl1000_hw_set_hw_params,
.nic_config = iwl1000_nic_config,
.temperature = iwlagn_temperature,
+ .support_ct_kill_exit = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
};
@@ -232,16 +235,56 @@ static void iwl2000_hw_set_hw_params(struct iwl_priv *priv)
priv->hw_params.sens = &iwl2000_sensitivity;
}
-struct iwl_lib_ops iwl2000_lib = {
+const struct iwl_dvm_cfg iwl_dvm_2000_cfg = {
.set_hw_params = iwl2000_hw_set_hw_params,
.nic_config = iwl2000_nic_config,
.temperature = iwlagn_temperature,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+ .hd_v2 = true,
+ .need_temp_offset_calib = true,
+ .temp_offset_v2 = true,
};
-struct iwl_lib_ops iwl2030_lib = {
+const struct iwl_dvm_cfg iwl_dvm_105_cfg = {
.set_hw_params = iwl2000_hw_set_hw_params,
.nic_config = iwl2000_nic_config,
.temperature = iwlagn_temperature,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+ .hd_v2 = true,
+ .need_temp_offset_calib = true,
+ .temp_offset_v2 = true,
+ .adv_pm = true,
+};
+
+static const struct iwl_dvm_bt_params iwl2030_bt_params = {
+ /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
+ .advanced_bt_coexist = true,
+ .agg_time_limit = BT_AGG_THRESHOLD_DEF,
+ .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
+ .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32,
+ .bt_sco_disable = true,
+ .bt_session_2 = true,
+};
+
+const struct iwl_dvm_cfg iwl_dvm_2030_cfg = {
+ .set_hw_params = iwl2000_hw_set_hw_params,
+ .nic_config = iwl2000_nic_config,
+ .temperature = iwlagn_temperature,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+ .hd_v2 = true,
+ .bt_params = &iwl2030_bt_params,
+ .need_temp_offset_calib = true,
+ .temp_offset_v2 = true,
+ .adv_pm = true,
};
/*
@@ -420,16 +463,23 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
return iwl_dvm_send_cmd(priv, &hcmd);
}
-struct iwl_lib_ops iwl5000_lib = {
+const struct iwl_dvm_cfg iwl_dvm_5000_cfg = {
.set_hw_params = iwl5000_hw_set_hw_params,
.set_channel_switch = iwl5000_hw_channel_switch,
.temperature = iwlagn_temperature,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+ .no_idle_support = true,
};
-struct iwl_lib_ops iwl5150_lib = {
+const struct iwl_dvm_cfg iwl_dvm_5150_cfg = {
.set_hw_params = iwl5150_hw_set_hw_params,
.set_channel_switch = iwl5000_hw_channel_switch,
.temperature = iwl5150_temperature,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+ .no_idle_support = true,
+ .no_xtal_calib = true,
};
@@ -584,16 +634,59 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
return err;
}
-struct iwl_lib_ops iwl6000_lib = {
+const struct iwl_dvm_cfg iwl_dvm_6000_cfg = {
.set_hw_params = iwl6000_hw_set_hw_params,
.set_channel_switch = iwl6000_hw_channel_switch,
.nic_config = iwl6000_nic_config,
.temperature = iwlagn_temperature,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+};
+
+const struct iwl_dvm_cfg iwl_dvm_6005_cfg = {
+ .set_hw_params = iwl6000_hw_set_hw_params,
+ .set_channel_switch = iwl6000_hw_channel_switch,
+ .nic_config = iwl6000_nic_config,
+ .temperature = iwlagn_temperature,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+ .need_temp_offset_calib = true,
+};
+
+const struct iwl_dvm_cfg iwl_dvm_6050_cfg = {
+ .set_hw_params = iwl6000_hw_set_hw_params,
+ .set_channel_switch = iwl6000_hw_channel_switch,
+ .nic_config = iwl6000_nic_config,
+ .temperature = iwlagn_temperature,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ .chain_noise_scale = 1500,
+};
+
+static const struct iwl_dvm_bt_params iwl6000_bt_params = {
+ /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
+ .advanced_bt_coexist = true,
+ .agg_time_limit = BT_AGG_THRESHOLD_DEF,
+ .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
+ .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT,
+ .bt_sco_disable = true,
};
-struct iwl_lib_ops iwl6030_lib = {
+const struct iwl_dvm_cfg iwl_dvm_6030_cfg = {
.set_hw_params = iwl6000_hw_set_hw_params,
.set_channel_switch = iwl6000_hw_channel_switch,
.nic_config = iwl6000_nic_config,
.temperature = iwlagn_temperature,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ .chain_noise_scale = 1000,
+ .bt_params = &iwl6000_bt_params,
+ .need_temp_offset_calib = true,
+ .adv_pm = true,
};
diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c
index 54f553380aa8..9879550a0fea 100644
--- a/drivers/net/wireless/iwlwifi/dvm/lib.c
+++ b/drivers/net/wireless/iwlwifi/dvm/lib.c
@@ -254,23 +254,23 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) !=
sizeof(basic.bt3_lookup_table));
- if (priv->cfg->bt_params) {
+ if (priv->lib->bt_params) {
/*
* newer generation of devices (2000 series and newer)
* use the version 2 of the bt command
* we need to make sure sending the host command
* with correct data structure to avoid uCode assert
*/
- if (priv->cfg->bt_params->bt_session_2) {
+ if (priv->lib->bt_params->bt_session_2) {
bt_cmd_v2.prio_boost = cpu_to_le32(
- priv->cfg->bt_params->bt_prio_boost);
+ priv->lib->bt_params->bt_prio_boost);
bt_cmd_v2.tx_prio_boost = 0;
bt_cmd_v2.rx_prio_boost = 0;
} else {
/* older version only has 8 bits */
- WARN_ON(priv->cfg->bt_params->bt_prio_boost & ~0xFF);
+ WARN_ON(priv->lib->bt_params->bt_prio_boost & ~0xFF);
bt_cmd_v1.prio_boost =
- priv->cfg->bt_params->bt_prio_boost;
+ priv->lib->bt_params->bt_prio_boost;
bt_cmd_v1.tx_prio_boost = 0;
bt_cmd_v1.rx_prio_boost = 0;
}
@@ -330,7 +330,7 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
priv->bt_full_concurrent ?
"full concurrency" : "3-wire");
- if (priv->cfg->bt_params->bt_session_2) {
+ if (priv->lib->bt_params->bt_session_2) {
memcpy(&bt_cmd_v2.basic, &basic,
sizeof(basic));
ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
@@ -758,8 +758,8 @@ static bool is_single_rx_stream(struct iwl_priv *priv)
*/
static int iwl_get_active_rx_chain_count(struct iwl_priv *priv)
{
- if (priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist &&
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist &&
(priv->bt_full_concurrent ||
priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) {
/*
@@ -830,8 +830,8 @@ void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
else
active_chains = priv->nvm_data->valid_rx_ant;
- if (priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist &&
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist &&
(priv->bt_full_concurrent ||
priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) {
/*
diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
index cab23af0be9e..c0039a992909 100644
--- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
@@ -426,7 +426,11 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw,
if (ret)
goto error;
- iwl_trans_d3_suspend(priv->trans);
+ /* let the ucode operate on its own */
+ iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET,
+ CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
+
+ iwl_trans_d3_suspend(priv->trans, false);
goto out;
@@ -500,7 +504,7 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
/* we'll clear ctx->vif during iwlagn_prepare_restart() */
vif = ctx->vif;
- ret = iwl_trans_d3_resume(priv->trans, &d3_status);
+ ret = iwl_trans_d3_resume(priv->trans, &d3_status, false);
if (ret)
goto out_unlock;
@@ -509,6 +513,10 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
goto out_unlock;
}
+ /* uCode is no longer operating by itself */
+ iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR,
+ CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
+
base = priv->device_pointers.error_event_table;
if (!iwlagn_hw_valid_rtc_data_addr(base)) {
IWL_WARN(priv, "Invalid error table during resume!\n");
@@ -1276,8 +1284,8 @@ static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw,
IWL_DEBUG_MAC80211(priv, "enter\n");
mutex_lock(&priv->mutex);
- if (priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist) {
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist) {
if (rssi_event == RSSI_EVENT_LOW)
priv->bt_enable_pspoll = true;
else if (rssi_event == RSSI_EVENT_HIGH)
@@ -1387,7 +1395,7 @@ static int iwl_setup_interface(struct iwl_priv *priv,
return err;
}
- if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist &&
+ if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist &&
vif->type == NL80211_IFTYPE_ADHOC) {
/*
* pretend to have high BT traffic as long as we
diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c
index 74d7572e7091..68f754659570 100644
--- a/drivers/net/wireless/iwlwifi/dvm/main.c
+++ b/drivers/net/wireless/iwlwifi/dvm/main.c
@@ -615,7 +615,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv)
priv->thermal_throttle.ct_kill_toggle = false;
- if (priv->cfg->base_params->support_ct_kill_exit) {
+ if (priv->lib->support_ct_kill_exit) {
adv_cmd.critical_temperature_enter =
cpu_to_le32(priv->hw_params.ct_kill_threshold);
adv_cmd.critical_temperature_exit =
@@ -732,10 +732,10 @@ int iwl_alive_start(struct iwl_priv *priv)
}
/* download priority table before any calibration request */
- if (priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist) {
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist) {
/* Configure Bluetooth device coexistence support */
- if (priv->cfg->bt_params->bt_sco_disable)
+ if (priv->lib->bt_params->bt_sco_disable)
priv->bt_enable_pspoll = false;
else
priv->bt_enable_pspoll = true;
@@ -873,9 +873,9 @@ void iwl_down(struct iwl_priv *priv)
priv->bt_status = 0;
priv->cur_rssi_ctx = NULL;
priv->bt_is_sco = 0;
- if (priv->cfg->bt_params)
+ if (priv->lib->bt_params)
priv->bt_traffic_load =
- priv->cfg->bt_params->bt_init_traffic_load;
+ priv->lib->bt_params->bt_init_traffic_load;
else
priv->bt_traffic_load = 0;
priv->bt_full_concurrent = false;
@@ -1058,7 +1058,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
iwl_setup_scan_deferred_work(priv);
- if (priv->cfg->bt_params)
+ if (priv->lib->bt_params)
iwlagn_bt_setup_deferred_work(priv);
init_timer(&priv->statistics_periodic);
@@ -1072,7 +1072,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
void iwl_cancel_deferred_work(struct iwl_priv *priv)
{
- if (priv->cfg->bt_params)
+ if (priv->lib->bt_params)
iwlagn_bt_cancel_deferred_work(priv);
cancel_work_sync(&priv->run_time_calib_work);
@@ -1098,8 +1098,7 @@ static int iwl_init_drv(struct iwl_priv *priv)
priv->band = IEEE80211_BAND_2GHZ;
- priv->plcp_delta_threshold =
- priv->cfg->base_params->plcp_delta_threshold;
+ priv->plcp_delta_threshold = priv->lib->plcp_delta_threshold;
priv->iw_mode = NL80211_IFTYPE_STATION;
priv->current_ht_config.smps = IEEE80211_SMPS_STATIC;
@@ -1116,8 +1115,8 @@ static int iwl_init_drv(struct iwl_priv *priv)
iwl_init_scan_params(priv);
/* init bt coex */
- if (priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist) {
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist) {
priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT;
priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT;
priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK;
@@ -1264,31 +1263,37 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
switch (priv->cfg->device_family) {
case IWL_DEVICE_FAMILY_1000:
case IWL_DEVICE_FAMILY_100:
- priv->lib = &iwl1000_lib;
+ priv->lib = &iwl_dvm_1000_cfg;
break;
case IWL_DEVICE_FAMILY_2000:
+ priv->lib = &iwl_dvm_2000_cfg;
+ break;
case IWL_DEVICE_FAMILY_105:
- priv->lib = &iwl2000_lib;
+ priv->lib = &iwl_dvm_105_cfg;
break;
case IWL_DEVICE_FAMILY_2030:
case IWL_DEVICE_FAMILY_135:
- priv->lib = &iwl2030_lib;
+ priv->lib = &iwl_dvm_2030_cfg;
break;
case IWL_DEVICE_FAMILY_5000:
- priv->lib = &iwl5000_lib;
+ priv->lib = &iwl_dvm_5000_cfg;
break;
case IWL_DEVICE_FAMILY_5150:
- priv->lib = &iwl5150_lib;
+ priv->lib = &iwl_dvm_5150_cfg;
break;
case IWL_DEVICE_FAMILY_6000:
- case IWL_DEVICE_FAMILY_6005:
case IWL_DEVICE_FAMILY_6000i:
+ priv->lib = &iwl_dvm_6000_cfg;
+ break;
+ case IWL_DEVICE_FAMILY_6005:
+ priv->lib = &iwl_dvm_6005_cfg;
+ break;
case IWL_DEVICE_FAMILY_6050:
case IWL_DEVICE_FAMILY_6150:
- priv->lib = &iwl6000_lib;
+ priv->lib = &iwl_dvm_6050_cfg;
break;
case IWL_DEVICE_FAMILY_6030:
- priv->lib = &iwl6030_lib;
+ priv->lib = &iwl_dvm_6030_cfg;
break;
default:
break;
diff --git a/drivers/net/wireless/iwlwifi/dvm/power.c b/drivers/net/wireless/iwlwifi/dvm/power.c
index bd69018d07a9..77cb59712235 100644
--- a/drivers/net/wireless/iwlwifi/dvm/power.c
+++ b/drivers/net/wireless/iwlwifi/dvm/power.c
@@ -163,7 +163,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
u8 skip;
u32 slp_itrvl;
- if (priv->cfg->adv_pm) {
+ if (priv->lib->adv_pm) {
table = apm_range_2;
if (period <= IWL_DTIM_RANGE_1_MAX)
table = apm_range_1;
@@ -217,7 +217,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA;
if (iwl_advanced_bt_coexist(priv)) {
- if (!priv->cfg->bt_params->bt_sco_disable)
+ if (!priv->lib->bt_params->bt_sco_disable)
cmd->flags |= IWL_POWER_BT_SCO_ENA;
else
cmd->flags &= ~IWL_POWER_BT_SCO_ENA;
@@ -293,7 +293,7 @@ static void iwl_power_build_cmd(struct iwl_priv *priv,
if (priv->wowlan)
iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper);
- else if (!priv->cfg->base_params->no_idle_support &&
+ else if (!priv->lib->no_idle_support &&
priv->hw->conf.flags & IEEE80211_CONF_IDLE)
iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20);
else if (iwl_tt_is_low_power_state(priv)) {
diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c
index 907bd6e50aad..94314a8e1029 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rs.c
+++ b/drivers/net/wireless/iwlwifi/dvm/rs.c
@@ -1088,7 +1088,7 @@ done:
(priv->tm_fixed_rate != lq_sta->dbg_fixed_rate))
rs_program_fix_rate(priv, lq_sta);
#endif
- if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist)
+ if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist)
rs_bt_update_lq(priv, ctx, lq_sta);
}
@@ -3064,11 +3064,11 @@ static void rs_fill_link_cmd(struct iwl_priv *priv,
* overwrite if needed, pass aggregation time limit
* to uCode in uSec
*/
- if (priv && priv->cfg->bt_params &&
- priv->cfg->bt_params->agg_time_limit &&
+ if (priv && priv->lib->bt_params &&
+ priv->lib->bt_params->agg_time_limit &&
priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)
lq_cmd->agg_params.agg_time_limit =
- cpu_to_le16(priv->cfg->bt_params->agg_time_limit);
+ cpu_to_le16(priv->lib->bt_params->agg_time_limit);
}
static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c
index a4eed2055fdb..2f3fd160ab44 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rx.c
+++ b/drivers/net/wireless/iwlwifi/dvm/rx.c
@@ -1102,7 +1102,7 @@ void iwl_setup_rx_handlers(struct iwl_priv *priv)
iwl_notification_wait_init(&priv->notif_wait);
/* Set up BT Rx handlers */
- if (priv->cfg->bt_params)
+ if (priv->lib->bt_params)
iwlagn_bt_rx_handler_setup(priv);
}
diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c
index d69b55866714..8c686a5b90ac 100644
--- a/drivers/net/wireless/iwlwifi/dvm/scan.c
+++ b/drivers/net/wireless/iwlwifi/dvm/scan.c
@@ -801,8 +801,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
* Internal scans are passive, so we can indiscriminately set
* the BT ignore flag on 2.4 GHz since it applies to TX only.
*/
- if (priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist)
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist)
scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT;
break;
case IEEE80211_BAND_5GHZ:
@@ -844,8 +844,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
band = priv->scan_band;
if (band == IEEE80211_BAND_2GHZ &&
- priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist) {
+ priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist) {
/* transmit 2.4 GHz probes only on first antenna */
scan_tx_antennas = first_antenna(scan_tx_antennas);
}
@@ -873,8 +873,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
rx_ant = first_antenna(active_chains);
}
- if (priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist &&
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist &&
priv->bt_full_concurrent) {
/* operated as 1x1 in full concurrency mode */
rx_ant = first_antenna(rx_ant);
diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.c b/drivers/net/wireless/iwlwifi/dvm/tt.c
index 03f9bc01c0cc..fbeee081ee2f 100644
--- a/drivers/net/wireless/iwlwifi/dvm/tt.c
+++ b/drivers/net/wireless/iwlwifi/dvm/tt.c
@@ -627,7 +627,7 @@ void iwl_tt_initialize(struct iwl_priv *priv)
INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter);
INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit);
- if (priv->cfg->base_params->adv_thermal_throttle) {
+ if (priv->lib->adv_thermal_throttle) {
IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n");
tt->restriction = kcalloc(IWL_TI_STATE_MAX,
sizeof(struct iwl_tt_restriction),
diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c
index a900aaf47790..353a053b4eb1 100644
--- a/drivers/net/wireless/iwlwifi/dvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/dvm/tx.c
@@ -83,8 +83,8 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
else if (ieee80211_is_back_req(fc))
tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK;
else if (info->band == IEEE80211_BAND_2GHZ &&
- priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist &&
+ priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist &&
(ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) ||
ieee80211_is_reassoc_req(fc) ||
skb->protocol == cpu_to_be16(ETH_P_PAE)))
@@ -202,8 +202,8 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
rate_flags |= RATE_MCS_CCK_MSK;
/* Set up antennas */
- if (priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist &&
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist &&
priv->bt_full_concurrent) {
/* operated as 1x1 in full concurrency mode */
priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
@@ -986,8 +986,8 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv,
* notification again.
*/
if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 &&
- priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist) {
+ priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist) {
IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n");
}
diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c
index 0a1cdc5e856b..86270b69cd02 100644
--- a/drivers/net/wireless/iwlwifi/dvm/ucode.c
+++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c
@@ -132,8 +132,8 @@ int iwl_init_alive_start(struct iwl_priv *priv)
{
int ret;
- if (priv->cfg->bt_params &&
- priv->cfg->bt_params->advanced_bt_coexist) {
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist) {
/*
* Tell uCode we are ready to perform calibration
* need to perform this before any calibration
@@ -155,8 +155,8 @@ int iwl_init_alive_start(struct iwl_priv *priv)
* temperature offset calibration is only needed for runtime ucode,
* so prepare the value now.
*/
- if (priv->cfg->need_temp_offset_calib) {
- if (priv->cfg->temp_offset_v2)
+ if (priv->lib->need_temp_offset_calib) {
+ if (priv->lib->temp_offset_v2)
return iwl_set_temperature_offset_calib_v2(priv);
else
return iwl_set_temperature_offset_calib(priv);
@@ -277,7 +277,7 @@ static int iwl_alive_notify(struct iwl_priv *priv)
if (ret)
return ret;
- if (!priv->cfg->no_xtal_calib) {
+ if (!priv->lib->no_xtal_calib) {
ret = iwl_set_Xtal_calib(priv);
if (ret)
return ret;
diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c
index c080ae3070b2..0d2afe098afc 100644
--- a/drivers/net/wireless/iwlwifi/iwl-1000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-1000.c
@@ -60,9 +60,6 @@ static const struct iwl_base_params iwl1000_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_1000,
.shadow_ram_support = false,
.led_compensation = 51,
- .support_ct_kill_exit = true,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
- .chain_noise_scale = 1000,
.wd_timeout = IWL_WATCHDOG_DISABLED,
.max_event_log_size = 128,
};
diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c
index a6ddd2f9fba0..c727ec7c90a6 100644
--- a/drivers/net/wireless/iwlwifi/iwl-2000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-2000.c
@@ -72,14 +72,9 @@ static const struct iwl_base_params iwl2000_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_2x00,
.shadow_ram_support = true,
.led_compensation = 51,
- .adv_thermal_throttle = true,
- .support_ct_kill_exit = true,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
- .chain_noise_scale = 1000,
.wd_timeout = IWL_DEF_WD_TIMEOUT,
.max_event_log_size = 512,
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */
- .hd_v2 = true,
};
@@ -90,14 +85,9 @@ static const struct iwl_base_params iwl2030_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_2x00,
.shadow_ram_support = true,
.led_compensation = 57,
- .adv_thermal_throttle = true,
- .support_ct_kill_exit = true,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
- .chain_noise_scale = 1000,
.wd_timeout = IWL_LONG_WD_TIMEOUT,
.max_event_log_size = 512,
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */
- .hd_v2 = true,
};
static const struct iwl_ht_params iwl2000_ht_params = {
@@ -106,16 +96,6 @@ static const struct iwl_ht_params iwl2000_ht_params = {
.ht40_bands = BIT(IEEE80211_BAND_2GHZ),
};
-static const struct iwl_bt_params iwl2030_bt_params = {
- /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
- .advanced_bt_coexist = true,
- .agg_time_limit = BT_AGG_THRESHOLD_DEF,
- .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
- .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32,
- .bt_sco_disable = true,
- .bt_session_2 = true,
-};
-
static const struct iwl_eeprom_params iwl20x0_eeprom_params = {
.regulatory_bands = {
EEPROM_REG_BAND_1_CHANNELS,
@@ -137,12 +117,10 @@ static const struct iwl_eeprom_params iwl20x0_eeprom_params = {
.device_family = IWL_DEVICE_FAMILY_2000, \
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
- .nvm_ver = EEPROM_2000_EEPROM_VERSION, \
- .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
+ .nvm_ver = EEPROM_2000_EEPROM_VERSION, \
+ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
.base_params = &iwl2000_base_params, \
.eeprom_params = &iwl20x0_eeprom_params, \
- .need_temp_offset_calib = true, \
- .temp_offset_v2 = true, \
.led_mode = IWL_LED_RF_STATE
const struct iwl_cfg iwl2000_2bgn_cfg = {
@@ -168,12 +146,8 @@ const struct iwl_cfg iwl2000_2bgn_d_cfg = {
.nvm_ver = EEPROM_2000_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
.base_params = &iwl2030_base_params, \
- .bt_params = &iwl2030_bt_params, \
.eeprom_params = &iwl20x0_eeprom_params, \
- .need_temp_offset_calib = true, \
- .temp_offset_v2 = true, \
- .led_mode = IWL_LED_RF_STATE, \
- .adv_pm = true
+ .led_mode = IWL_LED_RF_STATE
const struct iwl_cfg iwl2030_2bgn_cfg = {
.name = "Intel(R) Centrino(R) Wireless-N 2230 BGN",
@@ -193,10 +167,7 @@ const struct iwl_cfg iwl2030_2bgn_cfg = {
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
.base_params = &iwl2000_base_params, \
.eeprom_params = &iwl20x0_eeprom_params, \
- .need_temp_offset_calib = true, \
- .temp_offset_v2 = true, \
.led_mode = IWL_LED_RF_STATE, \
- .adv_pm = true, \
.rx_with_siso_diversity = true
const struct iwl_cfg iwl105_bgn_cfg = {
@@ -222,12 +193,8 @@ const struct iwl_cfg iwl105_bgn_d_cfg = {
.nvm_ver = EEPROM_2000_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
.base_params = &iwl2030_base_params, \
- .bt_params = &iwl2030_bt_params, \
.eeprom_params = &iwl20x0_eeprom_params, \
- .need_temp_offset_calib = true, \
- .temp_offset_v2 = true, \
.led_mode = IWL_LED_RF_STATE, \
- .adv_pm = true, \
.rx_with_siso_diversity = true
const struct iwl_cfg iwl135_bgn_cfg = {
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index 403f3f224bf6..ecc01e1a61a1 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -59,11 +59,8 @@ static const struct iwl_base_params iwl5000_base_params = {
.num_of_queues = IWLAGN_NUM_QUEUES,
.pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
.led_compensation = 51,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
- .chain_noise_scale = 1000,
.wd_timeout = IWL_WATCHDOG_DISABLED,
.max_event_log_size = 512,
- .no_idle_support = true,
};
static const struct iwl_ht_params iwl5000_ht_params = {
@@ -159,7 +156,6 @@ const struct iwl_cfg iwl5350_agn_cfg = {
.nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION, \
.base_params = &iwl5000_base_params, \
.eeprom_params = &iwl5000_eeprom_params, \
- .no_xtal_calib = true, \
.led_mode = IWL_LED_BLINK, \
.internal_wimax_coex = true
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c
index b5ab8d1bcac0..30d45e2fc193 100644
--- a/drivers/net/wireless/iwlwifi/iwl-6000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-6000.c
@@ -82,10 +82,6 @@ static const struct iwl_base_params iwl6000_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_6x00,
.shadow_ram_support = true,
.led_compensation = 51,
- .adv_thermal_throttle = true,
- .support_ct_kill_exit = true,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
- .chain_noise_scale = 1000,
.wd_timeout = IWL_DEF_WD_TIMEOUT,
.max_event_log_size = 512,
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */
@@ -98,10 +94,6 @@ static const struct iwl_base_params iwl6050_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_6x50,
.shadow_ram_support = true,
.led_compensation = 51,
- .adv_thermal_throttle = true,
- .support_ct_kill_exit = true,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
- .chain_noise_scale = 1500,
.wd_timeout = IWL_DEF_WD_TIMEOUT,
.max_event_log_size = 1024,
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */
@@ -114,10 +106,6 @@ static const struct iwl_base_params iwl6000_g2_base_params = {
.max_ll_items = OTP_MAX_LL_ITEMS_6x00,
.shadow_ram_support = true,
.led_compensation = 57,
- .adv_thermal_throttle = true,
- .support_ct_kill_exit = true,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
- .chain_noise_scale = 1000,
.wd_timeout = IWL_LONG_WD_TIMEOUT,
.max_event_log_size = 512,
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */
@@ -129,15 +117,6 @@ static const struct iwl_ht_params iwl6000_ht_params = {
.ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
};
-static const struct iwl_bt_params iwl6000_bt_params = {
- /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
- .advanced_bt_coexist = true,
- .agg_time_limit = BT_AGG_THRESHOLD_DEF,
- .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
- .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT,
- .bt_sco_disable = true,
-};
-
static const struct iwl_eeprom_params iwl6000_eeprom_params = {
.regulatory_bands = {
EEPROM_REG_BAND_1_CHANNELS,
@@ -163,7 +142,6 @@ static const struct iwl_eeprom_params iwl6000_eeprom_params = {
.nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION, \
.base_params = &iwl6000_g2_base_params, \
.eeprom_params = &iwl6000_eeprom_params, \
- .need_temp_offset_calib = true, \
.led_mode = IWL_LED_RF_STATE
const struct iwl_cfg iwl6005_2agn_cfg = {
@@ -217,11 +195,8 @@ const struct iwl_cfg iwl6005_2agn_mow2_cfg = {
.nvm_ver = EEPROM_6030_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \
.base_params = &iwl6000_g2_base_params, \
- .bt_params = &iwl6000_bt_params, \
.eeprom_params = &iwl6000_eeprom_params, \
- .need_temp_offset_calib = true, \
- .led_mode = IWL_LED_RF_STATE, \
- .adv_pm = true \
+ .led_mode = IWL_LED_RF_STATE
const struct iwl_cfg iwl6030_2agn_cfg = {
.name = "Intel(R) Centrino(R) Advanced-N 6230 AGN",
@@ -256,11 +231,8 @@ const struct iwl_cfg iwl6030_2bg_cfg = {
.nvm_ver = EEPROM_6030_EEPROM_VERSION, \
.nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \
.base_params = &iwl6000_g2_base_params, \
- .bt_params = &iwl6000_bt_params, \
.eeprom_params = &iwl6000_eeprom_params, \
- .need_temp_offset_calib = true, \
- .led_mode = IWL_LED_RF_STATE, \
- .adv_pm = true
+ .led_mode = IWL_LED_RF_STATE
const struct iwl_cfg iwl6035_2agn_cfg = {
.name = "Intel(R) Centrino(R) Advanced-N 6235 AGN",
diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c
index 50263e87fe15..d4f3b4864ab1 100644
--- a/drivers/net/wireless/iwlwifi/iwl-7000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-7000.c
@@ -96,13 +96,9 @@ static const struct iwl_base_params iwl7000_base_params = {
.pll_cfg_val = 0,
.shadow_ram_support = true,
.led_compensation = 57,
- .adv_thermal_throttle = true,
- .support_ct_kill_exit = true,
- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
- .chain_noise_scale = 1000,
.wd_timeout = IWL_LONG_WD_TIMEOUT,
.max_event_log_size = 512,
- .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+ .shadow_reg_enable = true,
};
static const struct iwl_ht_params iwl7000_ht_params = {
@@ -118,14 +114,11 @@ static const struct iwl_ht_params iwl7000_ht_params = {
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
.base_params = &iwl7000_base_params, \
- /* TODO: .bt_params? */ \
- .need_temp_offset_calib = true, \
- .led_mode = IWL_LED_RF_STATE, \
- .adv_pm = true \
+ .led_mode = IWL_LED_RF_STATE
const struct iwl_cfg iwl7260_2ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC7260",
+ .name = "Intel(R) Dual Band Wireless AC 7260",
.fw_name_pre = IWL7260_FW_PRE,
IWL_DEVICE_7000,
.ht_params = &iwl7000_ht_params,
@@ -133,8 +126,44 @@ const struct iwl_cfg iwl7260_2ac_cfg = {
.nvm_calib_ver = IWL7260_TX_POWER_VERSION,
};
-const struct iwl_cfg iwl3160_ac_cfg = {
- .name = "Intel(R) Dual Band Wireless AC3160",
+const struct iwl_cfg iwl7260_2n_cfg = {
+ .name = "Intel(R) Dual Band Wireless N 7260",
+ .fw_name_pre = IWL7260_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL7260_NVM_VERSION,
+ .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
+};
+
+const struct iwl_cfg iwl7260_n_cfg = {
+ .name = "Intel(R) Wireless N 7260",
+ .fw_name_pre = IWL7260_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL7260_NVM_VERSION,
+ .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
+};
+
+const struct iwl_cfg iwl3160_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 3160",
+ .fw_name_pre = IWL3160_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL3160_NVM_VERSION,
+ .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
+};
+
+const struct iwl_cfg iwl3160_2n_cfg = {
+ .name = "Intel(R) Dual Band Wireless N 3160",
+ .fw_name_pre = IWL3160_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL3160_NVM_VERSION,
+ .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
+};
+
+const struct iwl_cfg iwl3160_n_cfg = {
+ .name = "Intel(R) Wireless N 3160",
.fw_name_pre = IWL3160_FW_PRE,
IWL_DEVICE_7000,
.ht_params = &iwl7000_ht_params,
diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h
index c38aa8f77554..a193832fc790 100644
--- a/drivers/net/wireless/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/iwlwifi/iwl-config.h
@@ -136,17 +136,9 @@ enum iwl_led_mode {
* @led_compensation: compensate on the led on/off time per HW according
* to the deviation to achieve the desired led frequency.
* The detail algorithm is described in iwl-led.c
- * @chain_noise_num_beacons: number of beacons used to compute chain noise
- * @adv_thermal_throttle: support advance thermal throttle
- * @support_ct_kill_exit: support ct kill exit condition
- * @plcp_delta_threshold: plcp error rate threshold used to trigger
- * radio tuning when there is a high receiving plcp error rate
- * @chain_noise_scale: default chain noise scale used for gain computation
* @wd_timeout: TX queues watchdog timeout
* @max_event_log_size: size of event log buffer size for ucode event logging
* @shadow_reg_enable: HW shadow register support
- * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up
- * @no_idle_support: do not support idle mode
*/
struct iwl_base_params {
int eeprom_size;
@@ -157,31 +149,9 @@ struct iwl_base_params {
const u16 max_ll_items;
const bool shadow_ram_support;
u16 led_compensation;
- bool adv_thermal_throttle;
- bool support_ct_kill_exit;
- u8 plcp_delta_threshold;
- s32 chain_noise_scale;
unsigned int wd_timeout;
u32 max_event_log_size;
const bool shadow_reg_enable;
- const bool hd_v2;
- const bool no_idle_support;
-};
-
-/*
- * @advanced_bt_coexist: support advanced bt coexist
- * @bt_init_traffic_load: specify initial bt traffic load
- * @bt_prio_boost: default bt priority boost value
- * @agg_time_limit: maximum number of uSec in aggregation
- * @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode
- */
-struct iwl_bt_params {
- bool advanced_bt_coexist;
- u8 bt_init_traffic_load;
- u32 bt_prio_boost;
- u16 agg_time_limit;
- bool bt_sco_disable;
- bool bt_session_2;
};
/*
@@ -231,16 +201,10 @@ struct iwl_eeprom_params {
* @nvm_calib_ver: NVM calibration version
* @lib: pointer to the lib ops
* @base_params: pointer to basic parameters
- * @ht_params: point to ht patameters
- * @bt_params: pointer to bt parameters
- * @need_temp_offset_calib: need to perform temperature offset calibration
- * @no_xtal_calib: some devices do not need crystal calibration data,
- * don't send it to those
+ * @ht_params: point to ht parameters
* @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off)
- * @adv_pm: advance power management
* @rx_with_siso_diversity: 1x1 device with rx antenna diversity
* @internal_wimax_coex: internal wifi/wimax combo device
- * @temp_offset_v2: support v2 of temperature offset calibration
*
* We enable the driver to be backward compatible wrt. hardware features.
* API differences in uCode shouldn't be handled here but through TLVs
@@ -264,15 +228,10 @@ struct iwl_cfg {
const struct iwl_base_params *base_params;
/* params likely to change within a device family */
const struct iwl_ht_params *ht_params;
- const struct iwl_bt_params *bt_params;
const struct iwl_eeprom_params *eeprom_params;
- const bool need_temp_offset_calib; /* if used set to true */
- const bool no_xtal_calib;
enum iwl_led_mode led_mode;
- const bool adv_pm;
const bool rx_with_siso_diversity;
const bool internal_wimax_coex;
- const bool temp_offset_v2;
};
/*
@@ -320,6 +279,10 @@ extern const struct iwl_cfg iwl105_bgn_cfg;
extern const struct iwl_cfg iwl105_bgn_d_cfg;
extern const struct iwl_cfg iwl135_bgn_cfg;
extern const struct iwl_cfg iwl7260_2ac_cfg;
-extern const struct iwl_cfg iwl3160_ac_cfg;
+extern const struct iwl_cfg iwl7260_2n_cfg;
+extern const struct iwl_cfg iwl7260_n_cfg;
+extern const struct iwl_cfg iwl3160_2ac_cfg;
+extern const struct iwl_cfg iwl3160_2n_cfg;
+extern const struct iwl_cfg iwl3160_n_cfg;
#endif /* __IWL_CONFIG_H__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h
index 20e845d4da04..a276af476e2d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-csr.h
+++ b/drivers/net/wireless/iwlwifi/iwl-csr.h
@@ -472,4 +472,23 @@
#define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10)
#define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0)
+/*****************************************************************************
+ * 7000/3000 series SHR DTS addresses *
+ *****************************************************************************/
+
+/* Diode Results Register Structure: */
+enum dtd_diode_reg {
+ DTS_DIODE_REG_DIG_VAL = 0x000000FF, /* bits [7:0] */
+ DTS_DIODE_REG_VREF_LOW = 0x0000FF00, /* bits [15:8] */
+ DTS_DIODE_REG_VREF_HIGH = 0x00FF0000, /* bits [23:16] */
+ DTS_DIODE_REG_VREF_ID = 0x03000000, /* bits [25:24] */
+ DTS_DIODE_REG_PASS_ONCE = 0x80000000, /* bits [31:31] */
+ DTS_DIODE_REG_FLAGS_MSK = 0xFF000000, /* bits [31:24] */
+/* Those are the masks INSIDE the flags bit-field: */
+ DTS_DIODE_REG_FLAGS_VREFS_ID_POS = 0,
+ DTS_DIODE_REG_FLAGS_VREFS_ID = 0x00000003, /* bits [1:0] */
+ DTS_DIODE_REG_FLAGS_PASS_ONCE_POS = 7,
+ DTS_DIODE_REG_FLAGS_PASS_ONCE = 0x00000080, /* bits [7:7] */
+};
+
#endif /* !__iwl_csr_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c
index 39aad9893e0b..4f886133639a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.c
@@ -1234,6 +1234,9 @@ MODULE_PARM_DESC(wd_disable,
"Disable stuck queue watchdog timer 0=system default, "
"1=disable, 2=enable (default: 0)");
+module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO);
+MODULE_PARM_DESC(nvm_file, "NVM file name");
+
/*
* set bt_coex_active to true, uCode will do kill/defer
* every time the priority line is asserted (BT is sending signals on the
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
index 600c9fdd7f71..4c887f365908 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
@@ -732,17 +732,16 @@ int iwl_init_sband_channels(struct iwl_nvm_data *data,
void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
struct iwl_nvm_data *data,
struct ieee80211_sta_ht_cap *ht_info,
- enum ieee80211_band band)
+ enum ieee80211_band band,
+ u8 tx_chains, u8 rx_chains)
{
int max_bit_rate = 0;
- u8 rx_chains;
- u8 tx_chains;
- tx_chains = hweight8(data->valid_tx_ant);
+ tx_chains = hweight8(tx_chains);
if (cfg->rx_with_siso_diversity)
rx_chains = 1;
else
- rx_chains = hweight8(data->valid_rx_ant);
+ rx_chains = hweight8(rx_chains);
if (!(data->sku_cap_11n_enable) || !cfg->ht_params) {
ht_info->ht_supported = false;
@@ -806,7 +805,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
sband->n_bitrates = N_RATES_24;
n_used += iwl_init_sband_channels(data, sband, n_channels,
IEEE80211_BAND_2GHZ);
- iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ);
+ iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ,
+ data->valid_tx_ant, data->valid_rx_ant);
sband = &data->bands[IEEE80211_BAND_5GHZ];
sband->band = IEEE80211_BAND_5GHZ;
@@ -814,7 +814,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
sband->n_bitrates = N_RATES_52;
n_used += iwl_init_sband_channels(data, sband, n_channels,
IEEE80211_BAND_5GHZ);
- iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ);
+ iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ,
+ data->valid_tx_ant, data->valid_rx_ant);
if (n_channels != n_used)
IWL_ERR_DEV(dev, "EEPROM: used only %d of %d channels\n",
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
index 37f115390b19..d73304a23ec2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
@@ -133,6 +133,7 @@ int iwl_init_sband_channels(struct iwl_nvm_data *data,
void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
struct iwl_nvm_data *data,
struct ieee80211_sta_ht_cap *ht_info,
- enum ieee80211_band band);
+ enum ieee80211_band band,
+ u8 tx_chains, u8 rx_chains);
#endif /* __iwl_eeprom_parse_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h
index c4c446d41eb0..f844d5c748c0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw.h
@@ -106,11 +106,14 @@ enum iwl_ucode_type {
/*
* enumeration of ucode section.
- * This enumeration is used for legacy tlv style (before 16.0 uCode).
+ * This enumeration is used directly for older firmware (before 16.0).
+ * For new firmware, there can be up to 4 sections (see below) but the
+ * first one packaged into the firmware file is the DATA section and
+ * some debugging code accesses that.
*/
enum iwl_ucode_sec {
- IWL_UCODE_SECTION_INST,
IWL_UCODE_SECTION_DATA,
+ IWL_UCODE_SECTION_INST,
};
/*
* For 16.0 uCode and above, there is no differentiation between sections,
diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h
index d6f6c37c09fd..36dfe0919f6b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-modparams.h
+++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h
@@ -119,6 +119,7 @@ struct iwl_mod_params {
int ant_coupling;
bool bt_ch_announce;
bool auto_agg;
+ char *nvm_file;
};
#endif /* #__iwl_modparams_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
index 6199a0a597a6..acd2665afb8c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
@@ -89,6 +89,7 @@ enum nvm_sku_bits {
NVM_SKU_CAP_BAND_24GHZ = BIT(0),
NVM_SKU_CAP_BAND_52GHZ = BIT(1),
NVM_SKU_CAP_11N_ENABLE = BIT(2),
+ NVM_SKU_CAP_11AC_ENABLE = BIT(3),
};
/* radio config bits (actual values from NVM definition) */
@@ -258,8 +259,6 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
struct iwl_nvm_data *data,
struct ieee80211_sta_vht_cap *vht_cap)
{
- /* For now, assume new devices with NVM are VHT capable */
-
vht_cap->vht_supported = true;
vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 |
@@ -292,7 +291,8 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
}
static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
- struct iwl_nvm_data *data, const __le16 *nvm_sw)
+ struct iwl_nvm_data *data, const __le16 *nvm_sw,
+ bool enable_vht, u8 tx_chains, u8 rx_chains)
{
int n_channels = iwl_init_channel_map(dev, cfg, data,
&nvm_sw[NVM_CHANNELS]);
@@ -305,7 +305,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
sband->n_bitrates = N_RATES_24;
n_used += iwl_init_sband_channels(data, sband, n_channels,
IEEE80211_BAND_2GHZ);
- iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ);
+ iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ,
+ tx_chains, rx_chains);
sband = &data->bands[IEEE80211_BAND_5GHZ];
sband->band = IEEE80211_BAND_5GHZ;
@@ -313,8 +314,10 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
sband->n_bitrates = N_RATES_52;
n_used += iwl_init_sband_channels(data, sband, n_channels,
IEEE80211_BAND_5GHZ);
- iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ);
- iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap);
+ iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ,
+ tx_chains, rx_chains);
+ if (enable_vht)
+ iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap);
if (n_channels != n_used)
IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n",
@@ -324,7 +327,7 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
struct iwl_nvm_data *
iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
const __le16 *nvm_hw, const __le16 *nvm_sw,
- const __le16 *nvm_calib)
+ const __le16 *nvm_calib, u8 tx_chains, u8 rx_chains)
{
struct iwl_nvm_data *data;
u8 hw_addr[ETH_ALEN];
@@ -380,7 +383,8 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
data->hw_addr[4] = hw_addr[5];
data->hw_addr[5] = hw_addr[4];
- iwl_init_sbands(dev, cfg, data, nvm_sw);
+ iwl_init_sbands(dev, cfg, data, nvm_sw, sku & NVM_SKU_CAP_11AC_ENABLE,
+ tx_chains, rx_chains);
data->calib_version = 255; /* TODO:
this value will prevent some checks from
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
index e57fb989661e..3325059c52d4 100644
--- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
+++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
@@ -75,6 +75,6 @@
struct iwl_nvm_data *
iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
const __le16 *nvm_hw, const __le16 *nvm_sw,
- const __le16 *nvm_calib);
+ const __le16 *nvm_calib, u8 tx_chains, u8 rx_chains);
#endif /* __iwl_nvm_parse_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h
index 386f2a7c87cb..ff8cc75c189d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/iwlwifi/iwl-prph.h
@@ -100,6 +100,18 @@
/* Device system time */
#define DEVICE_SYSTEM_TIME_REG 0xA0206C
+/*****************************************************************************
+ * 7000/3000 series SHR DTS addresses *
+ *****************************************************************************/
+
+#define SHR_MISC_WFM_DTS_EN (0x00a10024)
+#define DTSC_CFG_MODE (0x00a10604)
+#define DTSC_VREF_AVG (0x00a10648)
+#define DTSC_VREF5_AVG (0x00a1064c)
+#define DTSC_CFG_MODE_PERIODIC (0x2)
+#define DTSC_PTAT_AVG (0x00a10650)
+
+
/**
* Tx Scheduler
*
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index 7a13790b5bfe..be4b2ac3dbbf 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -189,7 +189,8 @@ enum CMD_MODE {
CMD_SYNC = 0,
CMD_ASYNC = BIT(0),
CMD_WANT_SKB = BIT(1),
- CMD_ON_DEMAND = BIT(2),
+ CMD_SEND_IN_RFKILL = BIT(2),
+ CMD_ON_DEMAND = BIT(3),
};
#define DEF_CMD_PAYLOAD_SIZE 320
@@ -427,8 +428,9 @@ struct iwl_trans_ops {
void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr);
void (*stop_device)(struct iwl_trans *trans);
- void (*d3_suspend)(struct iwl_trans *trans);
- int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status);
+ void (*d3_suspend)(struct iwl_trans *trans, bool test);
+ int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status,
+ bool test);
int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
@@ -455,7 +457,7 @@ struct iwl_trans_ops {
int (*read_mem)(struct iwl_trans *trans, u32 addr,
void *buf, int dwords);
int (*write_mem)(struct iwl_trans *trans, u32 addr,
- void *buf, int dwords);
+ const void *buf, int dwords);
void (*configure)(struct iwl_trans *trans,
const struct iwl_trans_config *trans_cfg);
void (*set_pmi)(struct iwl_trans *trans, bool state);
@@ -587,17 +589,18 @@ static inline void iwl_trans_stop_device(struct iwl_trans *trans)
trans->state = IWL_TRANS_NO_FW;
}
-static inline void iwl_trans_d3_suspend(struct iwl_trans *trans)
+static inline void iwl_trans_d3_suspend(struct iwl_trans *trans, bool test)
{
might_sleep();
- trans->ops->d3_suspend(trans);
+ trans->ops->d3_suspend(trans, test);
}
static inline int iwl_trans_d3_resume(struct iwl_trans *trans,
- enum iwl_d3_status *status)
+ enum iwl_d3_status *status,
+ bool test)
{
might_sleep();
- return trans->ops->d3_resume(trans, status);
+ return trans->ops->d3_resume(trans, status, test);
}
static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
@@ -761,7 +764,7 @@ static inline u32 iwl_trans_read_mem32(struct iwl_trans *trans, u32 addr)
}
static inline int iwl_trans_write_mem(struct iwl_trans *trans, u32 addr,
- void *buf, int dwords)
+ const void *buf, int dwords)
{
return trans->ops->write_mem(trans, addr, buf, dwords);
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile
index 2acc44b40986..ff856e543ae8 100644
--- a/drivers/net/wireless/iwlwifi/mvm/Makefile
+++ b/drivers/net/wireless/iwlwifi/mvm/Makefile
@@ -3,7 +3,7 @@ iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o
iwlmvm-y += scan.o time-event.o rs.o
iwlmvm-y += power.o bt-coex.o
-iwlmvm-y += led.o
+iwlmvm-y += led.o tt.o
iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
diff --git a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c
index 810bfa5f6de0..9a4d94a1f90d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c
+++ b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c
@@ -174,7 +174,7 @@ static const __le32 iwl_tight_lookup[BT_COEX_LUT_SIZE] = {
static const __le32 iwl_loose_lookup[BT_COEX_LUT_SIZE] = {
cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaeaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0xaaaaaaaa),
cpu_to_le32(0xcc00ff28),
cpu_to_le32(0x0000aaaa),
@@ -351,6 +351,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
enum ieee80211_band band;
int ave_rssi;
+ lockdep_assert_held(&mvm->mutex);
if (vif->type != NL80211_IFTYPE_STATION)
return;
@@ -365,7 +366,8 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
smps_mode = IEEE80211_SMPS_AUTOMATIC;
if (band != IEEE80211_BAND_2GHZ) {
- ieee80211_request_smps(vif, smps_mode);
+ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+ smps_mode);
return;
}
@@ -380,7 +382,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
mvmvif->id, data->notif->bt_status,
data->notif->bt_traffic_load, smps_mode);
- ieee80211_request_smps(vif, smps_mode);
+ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
/* don't reduce the Tx power if in loose scheme */
if (is_loose_coex())
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c
index 16bbdcc8627a..7a2ef3f013fd 100644
--- a/drivers/net/wireless/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/iwlwifi/mvm/d3.c
@@ -63,6 +63,7 @@
#include <linux/etherdevice.h>
#include <linux/ip.h>
+#include <linux/fs.h>
#include <net/cfg80211.h>
#include <net/ipv6.h>
#include <net/tcp.h>
@@ -756,7 +757,9 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
return 0;
}
-int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
+ struct cfg80211_wowlan *wowlan,
+ bool test)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_d3_iter_data suspend_iter_data = {
@@ -769,7 +772,7 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {};
struct iwl_wowlan_tkip_params_cmd tkip_cmd = {};
- struct iwl_d3_manager_config d3_cfg_cmd = {
+ struct iwl_d3_manager_config d3_cfg_cmd_data = {
/*
* Program the minimum sleep time to 10 seconds, as many
* platforms have issues processing a wakeup signal while
@@ -777,17 +780,30 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
*/
.min_sleep_time = cpu_to_le32(10 * 1000 * 1000),
};
+ struct iwl_host_cmd d3_cfg_cmd = {
+ .id = D3_CONFIG_CMD,
+ .flags = CMD_SYNC | CMD_WANT_SKB,
+ .data[0] = &d3_cfg_cmd_data,
+ .len[0] = sizeof(d3_cfg_cmd_data),
+ };
struct wowlan_key_data key_data = {
.use_rsc_tsc = false,
.tkip = &tkip_cmd,
.use_tkip = false,
};
int ret, i;
+ int len __maybe_unused;
u16 seq;
u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT;
- if (WARN_ON(!wowlan))
+ if (!wowlan) {
+ /*
+ * mac80211 shouldn't get here, but for D3 test
+ * it doesn't warrant a warning
+ */
+ WARN_ON(!test);
return -EINVAL;
+ }
key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
if (!key_data.rsc_tsc)
@@ -1007,15 +1023,31 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
if (ret)
goto out;
+ ret = iwl_mvm_power_update_mode(mvm, vif);
+ if (ret)
+ goto out;
+
/* must be last -- this switches firmware state */
- ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SYNC,
- sizeof(d3_cfg_cmd), &d3_cfg_cmd);
+ ret = iwl_mvm_send_cmd(mvm, &d3_cfg_cmd);
if (ret)
goto out;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ len = le32_to_cpu(d3_cfg_cmd.resp_pkt->len_n_flags) &
+ FH_RSCSR_FRAME_SIZE_MSK;
+ if (len >= sizeof(u32) * 2) {
+ mvm->d3_test_pme_ptr =
+ le32_to_cpup((__le32 *)d3_cfg_cmd.resp_pkt->data);
+ } else if (test) {
+ /* in test mode we require the pointer */
+ ret = -EIO;
+ goto out;
+ }
+#endif
+ iwl_free_resp(&d3_cfg_cmd);
clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
- iwl_trans_d3_suspend(mvm->trans);
+ iwl_trans_d3_suspend(mvm->trans, test);
out:
mvm->aux_sta.sta_id = old_aux_sta_id;
mvm_ap_sta->sta_id = old_ap_sta_id;
@@ -1030,6 +1062,11 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
return ret;
}
+int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+{
+ return __iwl_mvm_suspend(hw, wowlan, false);
+}
+
static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
@@ -1214,9 +1251,28 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
iwl_free_resp(&cmd);
}
-int iwl_mvm_resume(struct ieee80211_hw *hw)
+static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
+{
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ const struct fw_img *img = &mvm->fw->img[IWL_UCODE_WOWLAN];
+ u32 len = img->sec[IWL_UCODE_SECTION_DATA].len;
+ u32 offs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+
+ if (!mvm->store_d3_resume_sram)
+ return;
+
+ if (!mvm->d3_resume_sram) {
+ mvm->d3_resume_sram = kzalloc(len, GFP_KERNEL);
+ if (!mvm->d3_resume_sram)
+ return;
+ }
+
+ iwl_trans_read_mem_bytes(mvm->trans, offs, mvm->d3_resume_sram, len);
+#endif
+}
+
+static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_d3_iter_data resume_iter_data = {
.mvm = mvm,
};
@@ -1236,7 +1292,7 @@ int iwl_mvm_resume(struct ieee80211_hw *hw)
vif = resume_iter_data.vif;
- ret = iwl_trans_d3_resume(mvm->trans, &d3_status);
+ ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test);
if (ret)
goto out_unlock;
@@ -1245,12 +1301,15 @@ int iwl_mvm_resume(struct ieee80211_hw *hw)
goto out_unlock;
}
+ /* query SRAM first in case we want event logging */
+ iwl_mvm_read_d3_sram(mvm);
+
iwl_mvm_query_wakeup_reasons(mvm, vif);
out_unlock:
mutex_unlock(&mvm->mutex);
- if (vif)
+ if (!test && vif)
ieee80211_resume_disconnect(vif);
/* return 1 to reconfigure the device */
@@ -1258,9 +1317,106 @@ int iwl_mvm_resume(struct ieee80211_hw *hw)
return 1;
}
+int iwl_mvm_resume(struct ieee80211_hw *hw)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ return __iwl_mvm_resume(mvm, false);
+}
+
void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
device_set_wakeup_enable(mvm->trans->dev, enabled);
}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file)
+{
+ struct iwl_mvm *mvm = inode->i_private;
+ int err;
+
+ if (mvm->d3_test_active)
+ return -EBUSY;
+
+ file->private_data = inode->i_private;
+
+ ieee80211_stop_queues(mvm->hw);
+ synchronize_net();
+
+ /* start pseudo D3 */
+ rtnl_lock();
+ err = __iwl_mvm_suspend(mvm->hw, mvm->hw->wiphy->wowlan_config, true);
+ rtnl_unlock();
+ if (err > 0)
+ err = -EINVAL;
+ if (err) {
+ ieee80211_wake_queues(mvm->hw);
+ return err;
+ }
+ mvm->d3_test_active = true;
+ return 0;
+}
+
+static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ u32 pme_asserted;
+
+ while (true) {
+ pme_asserted = iwl_trans_read_mem32(mvm->trans,
+ mvm->d3_test_pme_ptr);
+ if (pme_asserted)
+ break;
+ if (msleep_interruptible(100))
+ break;
+ }
+
+ return 0;
+}
+
+static void iwl_mvm_d3_test_disconn_work_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ if (vif->type == NL80211_IFTYPE_STATION)
+ ieee80211_connection_loss(vif);
+}
+
+static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file)
+{
+ struct iwl_mvm *mvm = inode->i_private;
+ int remaining_time = 10;
+
+ mvm->d3_test_active = false;
+ __iwl_mvm_resume(mvm, true);
+ iwl_abort_notification_waits(&mvm->notif_wait);
+ ieee80211_restart_hw(mvm->hw);
+
+ /* wait for restart and disconnect all interfaces */
+ while (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
+ remaining_time > 0) {
+ remaining_time--;
+ msleep(1000);
+ }
+
+ if (remaining_time == 0)
+ IWL_ERR(mvm, "Timed out waiting for HW restart to finish!\n");
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_d3_test_disconn_work_iter, NULL);
+
+ ieee80211_wake_queues(mvm->hw);
+
+ return 0;
+}
+
+const struct file_operations iwl_dbgfs_d3_test_ops = {
+ .llseek = no_llseek,
+ .open = iwl_mvm_d3_test_open,
+ .read = iwl_mvm_d3_test_read,
+ .release = iwl_mvm_d3_test_release,
+};
+#endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
index 2053dccefcd6..b7643c16201f 100644
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
@@ -145,15 +145,18 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
char *buf;
u8 *ptr;
+ if (!mvm->ucode_loaded)
+ return -EINVAL;
+
/* default is to dump the entire data segment */
if (!mvm->dbgfs_sram_offset && !mvm->dbgfs_sram_len) {
- mvm->dbgfs_sram_offset = 0x800000;
- if (!mvm->ucode_loaded)
- return -EINVAL;
img = &mvm->fw->img[mvm->cur_ucode];
- mvm->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+ ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+ len = img->sec[IWL_UCODE_SECTION_DATA].len;
+ } else {
+ ofs = mvm->dbgfs_sram_offset;
+ len = mvm->dbgfs_sram_len;
}
- len = mvm->dbgfs_sram_len;
bufsz = len * 4 + 256;
buf = kzalloc(bufsz, GFP_KERNEL);
@@ -167,12 +170,9 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
}
pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", len);
- pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n",
- mvm->dbgfs_sram_offset);
+ pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", ofs);
- iwl_trans_read_mem_bytes(mvm->trans,
- mvm->dbgfs_sram_offset,
- ptr, len);
+ iwl_trans_read_mem_bytes(mvm->trans, ofs, ptr, len);
for (ofs = 0; ofs < len; ofs += 16) {
pos += scnprintf(buf + pos, bufsz - pos, "0x%.4x ", ofs);
hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos,
@@ -300,6 +300,146 @@ static ssize_t iwl_dbgfs_power_down_d3_allow_write(struct file *file,
return count;
}
+static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ enum iwl_dbgfs_pm_mask param, int val)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm;
+
+ dbgfs_pm->mask |= param;
+
+ switch (param) {
+ case MVM_DEBUGFS_PM_KEEP_ALIVE: {
+ struct ieee80211_hw *hw = mvm->hw;
+ int dtimper = hw->conf.ps_dtim_period ?: 1;
+ int dtimper_msec = dtimper * vif->bss_conf.beacon_int;
+
+ IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val);
+ if (val * MSEC_PER_SEC < 3 * dtimper_msec) {
+ IWL_WARN(mvm,
+ "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n",
+ val * MSEC_PER_SEC, 3 * dtimper_msec);
+ }
+ dbgfs_pm->keep_alive_seconds = val;
+ break;
+ }
+ case MVM_DEBUGFS_PM_SKIP_OVER_DTIM:
+ IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n",
+ val ? "enabled" : "disabled");
+ dbgfs_pm->skip_over_dtim = val;
+ break;
+ case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS:
+ IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val);
+ dbgfs_pm->skip_dtim_periods = val;
+ break;
+ case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT:
+ IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val);
+ dbgfs_pm->rx_data_timeout = val;
+ break;
+ case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT:
+ IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val);
+ dbgfs_pm->tx_data_timeout = val;
+ break;
+ case MVM_DEBUGFS_PM_DISABLE_POWER_OFF:
+ IWL_DEBUG_POWER(mvm, "disable_power_off=%d\n", val);
+ dbgfs_pm->disable_power_off = val;
+ break;
+ }
+}
+
+static ssize_t iwl_dbgfs_pm_params_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->dbgfs_data;
+ enum iwl_dbgfs_pm_mask param;
+ char buf[32] = {};
+ int val;
+ int ret;
+
+ if (copy_from_user(buf, user_buf, sizeof(buf)))
+ return -EFAULT;
+
+ if (!strncmp("keep_alive=", buf, 11)) {
+ if (sscanf(buf + 11, "%d", &val) != 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_PM_KEEP_ALIVE;
+ } else if (!strncmp("skip_over_dtim=", buf, 15)) {
+ if (sscanf(buf + 15, "%d", &val) != 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM;
+ } else if (!strncmp("skip_dtim_periods=", buf, 18)) {
+ if (sscanf(buf + 18, "%d", &val) != 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS;
+ } else if (!strncmp("rx_data_timeout=", buf, 16)) {
+ if (sscanf(buf + 16, "%d", &val) != 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT;
+ } else if (!strncmp("tx_data_timeout=", buf, 16)) {
+ if (sscanf(buf + 16, "%d", &val) != 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT;
+ } else if (!strncmp("disable_power_off=", buf, 18)) {
+ if (sscanf(buf + 18, "%d", &val) != 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_PM_DISABLE_POWER_OFF;
+ } else {
+ return -EINVAL;
+ }
+
+ mutex_lock(&mvm->mutex);
+ iwl_dbgfs_update_pm(mvm, vif, param, val);
+ ret = iwl_mvm_power_update_mode(mvm, vif);
+ mutex_unlock(&mvm->mutex);
+
+ return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_pm_params_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->dbgfs_data;
+ struct iwl_powertable_cmd cmd = {};
+ char buf[256];
+ int bufsz = sizeof(buf);
+ int pos = 0;
+
+ iwl_mvm_power_build_cmd(mvm, vif, &cmd);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
+ (cmd.flags &
+ cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
+ 0 : 1);
+ pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
+ le32_to_cpu(cmd.skip_dtim_periods));
+ pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
+ iwlmvm_mod_params.power_scheme);
+ pos += scnprintf(buf+pos, bufsz-pos, "flags = %d\n",
+ le16_to_cpu(cmd.flags));
+ pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
+ cmd.keep_alive_seconds);
+
+ if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
+ pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
+ (cmd.flags &
+ cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ?
+ 1 : 0);
+ pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
+ le32_to_cpu(cmd.rx_data_timeout));
+ pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
+ le32_to_cpu(cmd.tx_data_timeout));
+ }
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
@@ -481,6 +621,255 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file,
return count;
}
+static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
+ enum iwl_dbgfs_bf_mask param, int value)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
+
+ dbgfs_bf->mask |= param;
+
+ switch (param) {
+ case MVM_DEBUGFS_BF_ENERGY_DELTA:
+ dbgfs_bf->bf_energy_delta = value;
+ break;
+ case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA:
+ dbgfs_bf->bf_roaming_energy_delta = value;
+ break;
+ case MVM_DEBUGFS_BF_ROAMING_STATE:
+ dbgfs_bf->bf_roaming_state = value;
+ break;
+ case MVM_DEBUGFS_BF_TEMPERATURE_DELTA:
+ dbgfs_bf->bf_temperature_delta = value;
+ break;
+ case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER:
+ dbgfs_bf->bf_enable_beacon_filter = value;
+ break;
+ case MVM_DEBUGFS_BF_DEBUG_FLAG:
+ dbgfs_bf->bf_debug_flag = value;
+ break;
+ case MVM_DEBUGFS_BF_ESCAPE_TIMER:
+ dbgfs_bf->bf_escape_timer = value;
+ break;
+ case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT:
+ dbgfs_bf->ba_enable_beacon_abort = value;
+ break;
+ case MVM_DEBUGFS_BA_ESCAPE_TIMER:
+ dbgfs_bf->ba_escape_timer = value;
+ break;
+ }
+}
+
+static ssize_t iwl_dbgfs_bf_params_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->dbgfs_data;
+ enum iwl_dbgfs_bf_mask param;
+ char buf[256];
+ int buf_size;
+ int value;
+ int ret = 0;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ if (!strncmp("bf_energy_delta=", buf, 16)) {
+ if (sscanf(buf+16, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < IWL_BF_ENERGY_DELTA_MIN ||
+ value > IWL_BF_ENERGY_DELTA_MAX)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BF_ENERGY_DELTA;
+ } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) {
+ if (sscanf(buf+24, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN ||
+ value > IWL_BF_ROAMING_ENERGY_DELTA_MAX)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA;
+ } else if (!strncmp("bf_roaming_state=", buf, 17)) {
+ if (sscanf(buf+17, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < IWL_BF_ROAMING_STATE_MIN ||
+ value > IWL_BF_ROAMING_STATE_MAX)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BF_ROAMING_STATE;
+ } else if (!strncmp("bf_temperature_delta=", buf, 21)) {
+ if (sscanf(buf+21, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < IWL_BF_TEMPERATURE_DELTA_MIN ||
+ value > IWL_BF_TEMPERATURE_DELTA_MAX)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BF_TEMPERATURE_DELTA;
+ } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) {
+ if (sscanf(buf+24, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < 0 || value > 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER;
+ } else if (!strncmp("bf_debug_flag=", buf, 14)) {
+ if (sscanf(buf+14, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < 0 || value > 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BF_DEBUG_FLAG;
+ } else if (!strncmp("bf_escape_timer=", buf, 16)) {
+ if (sscanf(buf+16, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < IWL_BF_ESCAPE_TIMER_MIN ||
+ value > IWL_BF_ESCAPE_TIMER_MAX)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BF_ESCAPE_TIMER;
+ } else if (!strncmp("ba_escape_timer=", buf, 16)) {
+ if (sscanf(buf+16, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < IWL_BA_ESCAPE_TIMER_MIN ||
+ value > IWL_BA_ESCAPE_TIMER_MAX)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BA_ESCAPE_TIMER;
+ } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) {
+ if (sscanf(buf+23, "%d", &value) != 1)
+ return -EINVAL;
+ if (value < 0 || value > 1)
+ return -EINVAL;
+ param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT;
+ } else {
+ return -EINVAL;
+ }
+
+ mutex_lock(&mvm->mutex);
+ iwl_dbgfs_update_bf(vif, param, value);
+ if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) {
+ ret = iwl_mvm_disable_beacon_filter(mvm, vif);
+ } else {
+ if (mvmvif->bf_enabled)
+ ret = iwl_mvm_enable_beacon_filter(mvm, vif);
+ else
+ ret = iwl_mvm_disable_beacon_filter(mvm, vif);
+ }
+ mutex_unlock(&mvm->mutex);
+
+ return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ char buf[256];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+ struct iwl_beacon_filter_cmd cmd = {
+ .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT,
+ .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT,
+ .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT,
+ .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT,
+ .bf_enable_beacon_filter = IWL_BF_ENABLE_BEACON_FILTER_DEFAULT,
+ .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT,
+ .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT),
+ .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT),
+ .ba_enable_beacon_abort = IWL_BA_ENABLE_BEACON_ABORT_DEFAULT,
+ };
+
+ iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
+ if (mvmvif->bf_enabled)
+ cmd.bf_enable_beacon_filter = 1;
+ else
+ cmd.bf_enable_beacon_filter = 0;
+
+ pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n",
+ cmd.bf_energy_delta);
+ pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n",
+ cmd.bf_roaming_energy_delta);
+ pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n",
+ cmd.bf_roaming_state);
+ pos += scnprintf(buf+pos, bufsz-pos, "bf_temperature_delta = %d\n",
+ cmd.bf_temperature_delta);
+ pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n",
+ cmd.bf_enable_beacon_filter);
+ pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n",
+ cmd.bf_debug_flag);
+ pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n",
+ cmd.bf_escape_timer);
+ pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n",
+ cmd.ba_escape_timer);
+ pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n",
+ cmd.ba_enable_beacon_abort);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static ssize_t iwl_dbgfs_d3_sram_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ char buf[8] = {};
+ int store;
+
+ if (copy_from_user(buf, user_buf, sizeof(buf)))
+ return -EFAULT;
+
+ if (sscanf(buf, "%d", &store) != 1)
+ return -EINVAL;
+
+ mvm->store_d3_resume_sram = store;
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ const struct fw_img *img;
+ int ofs, len, pos = 0;
+ size_t bufsz, ret;
+ char *buf;
+ u8 *ptr = mvm->d3_resume_sram;
+
+ img = &mvm->fw->img[IWL_UCODE_WOWLAN];
+ len = img->sec[IWL_UCODE_SECTION_DATA].len;
+
+ bufsz = len * 4 + 256;
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ pos += scnprintf(buf, bufsz, "D3 SRAM capture: %sabled\n",
+ mvm->store_d3_resume_sram ? "en" : "dis");
+
+ if (ptr) {
+ for (ofs = 0; ofs < len; ofs += 16) {
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "0x%.4x ", ofs);
+ hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos,
+ bufsz - pos, false);
+ pos += strlen(buf + pos);
+ if (bufsz - pos > 0)
+ buf[pos++] = '\n';
+ }
+ } else {
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "(no data captured)\n");
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+
+ kfree(buf);
+
+ return ret;
+}
+#endif
+
#define MVM_DEBUGFS_READ_FILE_OPS(name) \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.read = iwl_dbgfs_##name##_read, \
@@ -524,9 +913,14 @@ MVM_DEBUGFS_READ_FILE_OPS(bt_notif);
MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow);
MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow);
MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart);
+#ifdef CONFIG_PM_SLEEP
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram);
+#endif
/* Interface specific debugfs entries */
MVM_DEBUGFS_READ_FILE_OPS(mac_params);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params);
int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
{
@@ -542,6 +936,10 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
+#ifdef CONFIG_PM_SLEEP
+ MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR);
+#endif
/*
* Create a symlink with mac80211. It will be removed when mac80211
@@ -577,9 +975,19 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
return;
}
+ if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
+ vif->type == NL80211_IFTYPE_STATION && !vif->p2p)
+ MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR |
+ S_IRUSR);
+
MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir,
S_IRUSR);
+ if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
+ mvmvif == mvm->bf_allowed_vif)
+ MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir,
+ S_IRUSR | S_IWUSR);
+
/*
* Create symlink for convenience pointing to interface specific
* debugfs entries for the driver. For example, under
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
index 51e015d1dfb2..6f8b2c16ae17 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
@@ -75,13 +75,15 @@ enum iwl_d3_wakeup_flags {
* struct iwl_d3_manager_config - D3 manager configuration command
* @min_sleep_time: minimum sleep time (in usec)
* @wakeup_flags: wakeup flags, see &enum iwl_d3_wakeup_flags
+ * @wakeup_host_timer: force wakeup after this many seconds
*
* The structure is used for the D3_CONFIG_CMD command.
*/
struct iwl_d3_manager_config {
__le32 min_sleep_time;
__le32 wakeup_flags;
-} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_3 */
+ __le32 wakeup_host_timer;
+} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_4 */
/* TODO: OFFLOADS_QUERY_API_S_VER_1 */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
index d68640ea41d4..98b1feb43d38 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
@@ -71,7 +71,13 @@
#define MAC_INDEX_MIN_DRIVER 0
#define NUM_MAC_INDEX_DRIVER MAC_INDEX_AUX
-#define AC_NUM 4 /* Number of access categories */
+enum iwl_ac {
+ AC_BK,
+ AC_BE,
+ AC_VI,
+ AC_VO,
+ AC_NUM,
+};
/**
* enum iwl_mac_protection_flags - MAC context flags
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
index 81fe45f46be7..d8e19290b0f3 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
@@ -101,20 +101,107 @@ enum iwl_power_flags {
* @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to
* PSM transition - legacy PM
* @sleep_interval: not in use
- * @keep_alive_beacons: not in use
+ * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag
+ * is set. For example, if it is required to skip over
+ * one DTIM, this value need to be set to 2 (DTIM periods).
* @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
* Default: 80dbm
*/
struct iwl_powertable_cmd {
- /* PM_POWER_TABLE_CMD_API_S_VER_5 */
+ /* PM_POWER_TABLE_CMD_API_S_VER_6 */
__le16 flags;
u8 keep_alive_seconds;
u8 debug_flags;
__le32 rx_data_timeout;
__le32 tx_data_timeout;
__le32 sleep_interval[IWL_POWER_VEC_SIZE];
- __le32 keep_alive_beacons;
+ __le32 skip_dtim_periods;
__le32 lprx_rssi_threshold;
} __packed;
+/**
+ * struct iwl_beacon_filter_cmd
+ * REPLY_BEACON_FILTERING_CMD = 0xd2 (command)
+ * @id_and_color: MAC contex identifier
+ * @bf_energy_delta: Used for RSSI filtering, if in 'normal' state. Send beacon
+ * to driver if delta in Energy values calculated for this and last
+ * passed beacon is greater than this threshold. Zero value means that
+ * the Energy change is ignored for beacon filtering, and beacon will
+ * not be forced to be sent to driver regardless of this delta. Typical
+ * energy delta 5dB.
+ * @bf_roaming_energy_delta: Used for RSSI filtering, if in 'roaming' state.
+ * Send beacon to driver if delta in Energy values calculated for this
+ * and last passed beacon is greater than this threshold. Zero value
+ * means that the Energy change is ignored for beacon filtering while in
+ * Roaming state, typical energy delta 1dB.
+ * @bf_roaming_state: Used for RSSI filtering. If absolute Energy values
+ * calculated for current beacon is less than the threshold, use
+ * Roaming Energy Delta Threshold, otherwise use normal Energy Delta
+ * Threshold. Typical energy threshold is -72dBm.
+ * @bf_temperature_delta: Send Beacon to driver if delta in temperature values
+ * calculated for this and the last passed beacon is greater than this
+ * threshold. Zero value means that the temperature changeis ignored for
+ * beacon filtering; beacons will not be forced to be sent to driver
+ * regardless of whether its temerature has been changed.
+ * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled.
+ * @bf_filter_escape_timer: Send beacons to to driver if no beacons were passed
+ * for a specific period of time. Units: Beacons.
+ * @ba_escape_timer: Fully receive and parse beacon if no beacons were passed
+ * for a longer period of time then this escape-timeout. Units: Beacons.
+ * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled.
+ */
+struct iwl_beacon_filter_cmd {
+ u8 bf_energy_delta;
+ u8 bf_roaming_energy_delta;
+ u8 bf_roaming_state;
+ u8 bf_temperature_delta;
+ u8 bf_enable_beacon_filter;
+ u8 bf_debug_flag;
+ __le16 reserved1;
+ __le32 bf_escape_timer;
+ __le32 ba_escape_timer;
+ u8 ba_enable_beacon_abort;
+ u8 reserved2[3];
+} __packed;
+
+/* Beacon filtering and beacon abort */
+#define IWL_BF_ENERGY_DELTA_DEFAULT 5
+#define IWL_BF_ENERGY_DELTA_MAX 255
+#define IWL_BF_ENERGY_DELTA_MIN 0
+
+#define IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT 1
+#define IWL_BF_ROAMING_ENERGY_DELTA_MAX 255
+#define IWL_BF_ROAMING_ENERGY_DELTA_MIN 0
+
+#define IWL_BF_ROAMING_STATE_DEFAULT 72
+#define IWL_BF_ROAMING_STATE_MAX 255
+#define IWL_BF_ROAMING_STATE_MIN 0
+
+#define IWL_BF_TEMPERATURE_DELTA_DEFAULT 5
+#define IWL_BF_TEMPERATURE_DELTA_MAX 255
+#define IWL_BF_TEMPERATURE_DELTA_MIN 0
+
+#define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1
+
+#define IWL_BF_DEBUG_FLAG_DEFAULT 0
+
+#define IWL_BF_ESCAPE_TIMER_DEFAULT 50
+#define IWL_BF_ESCAPE_TIMER_MAX 1024
+#define IWL_BF_ESCAPE_TIMER_MIN 0
+
+#define IWL_BA_ESCAPE_TIMER_DEFAULT 3
+#define IWL_BA_ESCAPE_TIMER_MAX 1024
+#define IWL_BA_ESCAPE_TIMER_MIN 0
+
+#define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1
+
+#define IWL_BF_CMD_CONFIG_DEFAULTS \
+ .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT, \
+ .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT, \
+ .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT, \
+ .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT, \
+ .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT, \
+ .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT), \
+ .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT)
+
#endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
index 007a93b25bd7..6994232f5726 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
@@ -134,6 +134,7 @@ enum iwl_tx_flags {
#define TX_CMD_SEC_WEP 0x01
#define TX_CMD_SEC_CCM 0x02
#define TX_CMD_SEC_TKIP 0x03
+#define TX_CMD_SEC_MSK 0x07
#define TX_CMD_SEC_WEP_KEY_IDX_POS 6
#define TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0
#define TX_CMD_SEC_KEY128 0x08
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
index c6384555aab4..cbfb3beae783 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
@@ -139,6 +139,9 @@ enum {
/* Power */
POWER_TABLE_CMD = 0x77,
+ /* Thermal Throttling*/
+ REPLY_THERMAL_MNG_BACKOFF = 0x7e,
+
/* Scanning */
SCAN_REQUEST_CMD = 0x80,
SCAN_ABORT_CMD = 0x81,
@@ -161,6 +164,8 @@ enum {
CARD_STATE_CMD = 0xa0,
CARD_STATE_NOTIFICATION = 0xa1,
+ MISSED_BEACONS_NOTIFICATION = 0xa2,
+
REPLY_RX_PHY_CMD = 0xc0,
REPLY_RX_MPDU_CMD = 0xc1,
BA_NOTIF = 0xc5,
@@ -170,6 +175,8 @@ enum {
BT_COEX_PROT_ENV = 0xcd,
BT_PROFILE_NOTIFICATION = 0xce,
+ REPLY_BEACON_FILTERING_CMD = 0xd2,
+
REPLY_DEBUG_CMD = 0xf0,
DEBUG_LOG_MSG = 0xf7,
@@ -938,6 +945,24 @@ struct iwl_card_state_notif {
} __packed; /* CARD_STATE_NTFY_API_S_VER_1 */
/**
+ * struct iwl_missed_beacons_notif - information on missed beacons
+ * ( MISSED_BEACONS_NOTIFICATION = 0xa2 )
+ * @mac_id: interface ID
+ * @consec_missed_beacons_since_last_rx: number of consecutive missed
+ * beacons since last RX.
+ * @consec_missed_beacons: number of consecutive missed beacons
+ * @num_expected_beacons:
+ * @num_recvd_beacons:
+ */
+struct iwl_missed_beacons_notif {
+ __le32 mac_id;
+ __le32 consec_missed_beacons_since_last_rx;
+ __le32 consec_missed_beacons;
+ __le32 num_expected_beacons;
+ __le32 num_recvd_beacons;
+} __packed; /* MISSED_BEACON_NTFY_API_S_VER_3 */
+
+/**
* struct iwl_set_calib_default_cmd - set default value for calibration.
* ( SET_CALIB_DEFAULT_CMD = 0x8e )
* @calib_index: the calibration to set value for
@@ -975,4 +1000,212 @@ struct iwl_mcast_filter_cmd {
u8 addr_list[0];
} __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */
+struct mvm_statistics_dbg {
+ __le32 burst_check;
+ __le32 burst_count;
+ __le32 wait_for_silence_timeout_cnt;
+ __le32 reserved[3];
+} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */
+
+struct mvm_statistics_div {
+ __le32 tx_on_a;
+ __le32 tx_on_b;
+ __le32 exec_time;
+ __le32 probe_time;
+ __le32 rssi_ant;
+ __le32 reserved2;
+} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */
+
+struct mvm_statistics_general_common {
+ __le32 temperature; /* radio temperature */
+ __le32 temperature_m; /* radio voltage */
+ struct mvm_statistics_dbg dbg;
+ __le32 sleep_time;
+ __le32 slots_out;
+ __le32 slots_idle;
+ __le32 ttl_timestamp;
+ struct mvm_statistics_div div;
+ __le32 rx_enable_counter;
+ /*
+ * num_of_sos_states:
+ * count the number of times we have to re-tune
+ * in order to get out of bad PHY status
+ */
+ __le32 num_of_sos_states;
+} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */
+
+struct mvm_statistics_rx_non_phy {
+ __le32 bogus_cts; /* CTS received when not expecting CTS */
+ __le32 bogus_ack; /* ACK received when not expecting ACK */
+ __le32 non_bssid_frames; /* number of frames with BSSID that
+ * doesn't belong to the STA BSSID */
+ __le32 filtered_frames; /* count frames that were dumped in the
+ * filtering process */
+ __le32 non_channel_beacons; /* beacons with our bss id but not on
+ * our serving channel */
+ __le32 channel_beacons; /* beacons with our bss id and in our
+ * serving channel */
+ __le32 num_missed_bcon; /* number of missed beacons */
+ __le32 adc_rx_saturation_time; /* count in 0.8us units the time the
+ * ADC was in saturation */
+ __le32 ina_detection_search_time;/* total time (in 0.8us) searched
+ * for INA */
+ __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */
+ __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */
+ __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */
+ __le32 interference_data_flag; /* flag for interference data
+ * availability. 1 when data is
+ * available. */
+ __le32 channel_load; /* counts RX Enable time in uSec */
+ __le32 dsp_false_alarms; /* DSP false alarm (both OFDM
+ * and CCK) counter */
+ __le32 beacon_rssi_a;
+ __le32 beacon_rssi_b;
+ __le32 beacon_rssi_c;
+ __le32 beacon_energy_a;
+ __le32 beacon_energy_b;
+ __le32 beacon_energy_c;
+ __le32 num_bt_kills;
+ __le32 mac_id;
+ __le32 directed_data_mpdu;
+} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */
+
+struct mvm_statistics_rx_phy {
+ __le32 ina_cnt;
+ __le32 fina_cnt;
+ __le32 plcp_err;
+ __le32 crc32_err;
+ __le32 overrun_err;
+ __le32 early_overrun_err;
+ __le32 crc32_good;
+ __le32 false_alarm_cnt;
+ __le32 fina_sync_err_cnt;
+ __le32 sfd_timeout;
+ __le32 fina_timeout;
+ __le32 unresponded_rts;
+ __le32 rxe_frame_limit_overrun;
+ __le32 sent_ack_cnt;
+ __le32 sent_cts_cnt;
+ __le32 sent_ba_rsp_cnt;
+ __le32 dsp_self_kill;
+ __le32 mh_format_err;
+ __le32 re_acq_main_rssi_sum;
+ __le32 reserved;
+} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */
+
+struct mvm_statistics_rx_ht_phy {
+ __le32 plcp_err;
+ __le32 overrun_err;
+ __le32 early_overrun_err;
+ __le32 crc32_good;
+ __le32 crc32_err;
+ __le32 mh_format_err;
+ __le32 agg_crc32_good;
+ __le32 agg_mpdu_cnt;
+ __le32 agg_cnt;
+ __le32 unsupport_mcs;
+} __packed; /* STATISTICS_HT_RX_PHY_API_S_VER_1 */
+
+#define MAX_CHAINS 3
+
+struct mvm_statistics_tx_non_phy_agg {
+ __le32 ba_timeout;
+ __le32 ba_reschedule_frames;
+ __le32 scd_query_agg_frame_cnt;
+ __le32 scd_query_no_agg;
+ __le32 scd_query_agg;
+ __le32 scd_query_mismatch;
+ __le32 frame_not_ready;
+ __le32 underrun;
+ __le32 bt_prio_kill;
+ __le32 rx_ba_rsp_cnt;
+ __s8 txpower[MAX_CHAINS];
+ __s8 reserved;
+ __le32 reserved2;
+} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */
+
+struct mvm_statistics_tx_channel_width {
+ __le32 ext_cca_narrow_ch20[1];
+ __le32 ext_cca_narrow_ch40[2];
+ __le32 ext_cca_narrow_ch80[3];
+ __le32 ext_cca_narrow_ch160[4];
+ __le32 last_tx_ch_width_indx;
+ __le32 rx_detected_per_ch_width[4];
+ __le32 success_per_ch_width[4];
+ __le32 fail_per_ch_width[4];
+}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */
+
+struct mvm_statistics_tx {
+ __le32 preamble_cnt;
+ __le32 rx_detected_cnt;
+ __le32 bt_prio_defer_cnt;
+ __le32 bt_prio_kill_cnt;
+ __le32 few_bytes_cnt;
+ __le32 cts_timeout;
+ __le32 ack_timeout;
+ __le32 expected_ack_cnt;
+ __le32 actual_ack_cnt;
+ __le32 dump_msdu_cnt;
+ __le32 burst_abort_next_frame_mismatch_cnt;
+ __le32 burst_abort_missing_next_frame_cnt;
+ __le32 cts_timeout_collision;
+ __le32 ack_or_ba_timeout_collision;
+ struct mvm_statistics_tx_non_phy_agg agg;
+ struct mvm_statistics_tx_channel_width channel_width;
+} __packed; /* STATISTICS_TX_API_S_VER_4 */
+
+
+struct mvm_statistics_bt_activity {
+ __le32 hi_priority_tx_req_cnt;
+ __le32 hi_priority_tx_denied_cnt;
+ __le32 lo_priority_tx_req_cnt;
+ __le32 lo_priority_tx_denied_cnt;
+ __le32 hi_priority_rx_req_cnt;
+ __le32 hi_priority_rx_denied_cnt;
+ __le32 lo_priority_rx_req_cnt;
+ __le32 lo_priority_rx_denied_cnt;
+} __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */
+
+struct mvm_statistics_general {
+ struct mvm_statistics_general_common common;
+ __le32 beacon_filtered;
+ __le32 missed_beacons;
+ __s8 beacon_filter_everage_energy;
+ __s8 beacon_filter_reason;
+ __s8 beacon_filter_current_energy;
+ __s8 beacon_filter_reserved;
+ __le32 beacon_filter_delta_time;
+ struct mvm_statistics_bt_activity bt_activity;
+} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */
+
+struct mvm_statistics_rx {
+ struct mvm_statistics_rx_phy ofdm;
+ struct mvm_statistics_rx_phy cck;
+ struct mvm_statistics_rx_non_phy general;
+ struct mvm_statistics_rx_ht_phy ofdm_ht;
+} __packed; /* STATISTICS_RX_API_S_VER_3 */
+
+/*
+ * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command)
+ *
+ * By default, uCode issues this notification after receiving a beacon
+ * while associated. To disable this behavior, set DISABLE_NOTIF flag in the
+ * REPLY_STATISTICS_CMD 0x9c, above.
+ *
+ * Statistics counters continue to increment beacon after beacon, but are
+ * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD
+ * 0x9c with CLEAR_STATS bit set (see above).
+ *
+ * uCode also issues this notification during scans. uCode clears statistics
+ * appropriately so that each notification contains statistics for only the
+ * one channel that has just been scanned.
+ */
+
+struct iwl_notif_statistics { /* STATISTICS_NTFY_API_S_VER_8 */
+ __le32 flag;
+ struct mvm_statistics_rx rx;
+ struct mvm_statistics_tx tx;
+ struct mvm_statistics_general general;
+} __packed;
+
#endif /* __fw_api_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
index e18c92dd60ec..cd7c0032cc58 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
@@ -326,6 +326,17 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans);
WARN_ON(ret);
+ /*
+ * abort after reading the nvm in case RF Kill is on, we will complete
+ * the init seq later when RF kill will switch to off
+ */
+ if (iwl_mvm_is_radio_killed(mvm)) {
+ IWL_DEBUG_RF_KILL(mvm,
+ "jump over all phy activities due to RF kill\n");
+ iwl_remove_notification(&mvm->notif_wait, &calib_wait);
+ return 1;
+ }
+
/* Send TX valid antennas before triggering calibrations */
ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
if (ret)
@@ -388,6 +399,8 @@ out:
int iwl_mvm_up(struct iwl_mvm *mvm)
{
int ret, i;
+ struct ieee80211_channel *chan;
+ struct cfg80211_chan_def chandef;
lockdep_assert_held(&mvm->mutex);
@@ -400,8 +413,16 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
ret = iwl_run_init_mvm_ucode(mvm, false);
if (ret && !iwlmvm_mod_params.init_dbg) {
IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret);
+ /* this can't happen */
+ if (WARN_ON(ret > 0))
+ ret = -ERFKILL;
goto error;
}
+ /* should stop & start HW since that INIT image just loaded */
+ iwl_trans_stop_hw(mvm->trans, false);
+ ret = iwl_trans_start_hw(mvm->trans);
+ if (ret)
+ return ret;
}
if (iwlmvm_mod_params.init_dbg)
@@ -443,8 +464,22 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
if (ret)
goto error;
- IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
+ /* Add all the PHY contexts */
+ chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0];
+ cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+ for (i = 0; i < NUM_PHY_CTX; i++) {
+ /*
+ * The channel used here isn't relevant as it's
+ * going to be overwritten in the other flows.
+ * For now use the first channel we have.
+ */
+ ret = iwl_mvm_phy_ctxt_add(mvm, &mvm->phy_ctxts[i],
+ &chandef, 1, 1);
+ if (ret)
+ goto error;
+ }
+ IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
return 0;
error:
iwl_trans_stop_device(mvm->trans);
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
index b2cc3d98e0f7..46c7c0507c25 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
@@ -227,7 +227,7 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
.found_vif = false,
};
u32 ac;
- int ret;
+ int ret, i;
/*
* Allocate a MAC ID and a TSF for this MAC, along with the queues
@@ -335,6 +335,9 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT;
mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
+ for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++)
+ mvmvif->smps_requests[i] = IEEE80211_SMPS_AUTOMATIC;
+
return 0;
exit_fail:
@@ -362,7 +365,7 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
break;
case NL80211_IFTYPE_AP:
iwl_trans_ac_txq_enable(mvm->trans, vif->cab_queue,
- IWL_MVM_TX_FIFO_VO);
+ IWL_MVM_TX_FIFO_MCAST);
/* fall through */
default:
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
@@ -550,6 +553,10 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
cmd->ac[i].fifos_mask = BIT(iwl_mvm_ac_to_tx_fifo[i]);
}
+ /* in AP mode, the MCAST FIFO takes the EDCA params from VO */
+ if (vif->type == NL80211_IFTYPE_AP)
+ cmd->ac[AC_VO].fifos_mask |= BIT(IWL_MVM_TX_FIFO_MCAST);
+
if (vif->bss_conf.qos)
cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
@@ -1047,3 +1054,28 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
rate);
return 0;
}
+
+static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ u16 *id = _data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (mvmvif->id == *id)
+ ieee80211_beacon_loss(vif);
+}
+
+int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_missed_beacons_notif *missed_beacons = (void *)pkt->data;
+ u16 id = (u16)le32_to_cpu(missed_beacons->mac_id);
+
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_beacon_loss_iterator,
+ &id);
+ return 0;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index a5eb8c82f16a..2ed296caeb28 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -127,6 +127,17 @@ static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = {
};
#endif
+static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
+{
+ int i;
+
+ memset(mvm->phy_ctxts, 0, sizeof(mvm->phy_ctxts));
+ for (i = 0; i < NUM_PHY_CTX; i++) {
+ mvm->phy_ctxts[i].id = i;
+ mvm->phy_ctxts[i].ref = 0;
+ }
+}
+
int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
{
struct ieee80211_hw *hw = mvm->hw;
@@ -141,7 +152,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
IEEE80211_HW_AMPDU_AGGREGATION |
- IEEE80211_HW_TIMING_BEACON_ONLY;
+ IEEE80211_HW_TIMING_BEACON_ONLY |
+ IEEE80211_HW_CONNECTION_MONITOR;
hw->queues = IWL_MVM_FIRST_AGG_QUEUE;
hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
@@ -158,7 +170,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->sta_data_size = sizeof(struct iwl_mvm_sta);
hw->vif_data_size = sizeof(struct iwl_mvm_vif);
- hw->chanctx_data_size = sizeof(struct iwl_mvm_phy_ctxt);
+ hw->chanctx_data_size = sizeof(u16);
hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_P2P_CLIENT) |
@@ -193,6 +205,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->n_addresses++;
}
+ iwl_mvm_reset_phy_ctxts(mvm);
+
/* we create the 802.11 header and a max-length SSID element */
hw->wiphy->max_scan_ie_len =
mvm->fw->ucode_capa.max_probe_length - 24 - 34;
@@ -252,8 +266,8 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- if (test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status)) {
- IWL_DEBUG_DROP(mvm, "Dropping - RF KILL\n");
+ if (iwl_mvm_is_radio_killed(mvm)) {
+ IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n");
goto drop;
}
@@ -345,8 +359,7 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data);
spin_unlock_bh(&mvm->time_event_lock);
- if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
- mvmvif->phy_ctxt = NULL;
+ mvmvif->phy_ctxt = NULL;
}
static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
@@ -363,6 +376,9 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
iwl_mvm_cleanup_iterator, mvm);
+ mvm->p2p_device_vif = NULL;
+
+ iwl_mvm_reset_phy_ctxts(mvm);
memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained));
@@ -456,6 +472,20 @@ static void iwl_mvm_power_update_iterator(void *data, u8 *mac,
iwl_mvm_power_update_mode(mvm, vif);
}
+static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
+{
+ u16 i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ for (i = 0; i < NUM_PHY_CTX; i++)
+ if (!mvm->phy_ctxts[i].ref)
+ return &mvm->phy_ctxts[i];
+
+ IWL_ERR(mvm, "No available PHY context\n");
+ return NULL;
+}
+
static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
@@ -530,32 +560,34 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
*/
iwl_mvm_power_update_mode(mvm, vif);
+ /* beacon filtering */
+ if (!mvm->bf_allowed_vif &&
+ vif->type == NL80211_IFTYPE_STATION && !vif->p2p){
+ mvm->bf_allowed_vif = mvmvif;
+ vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
+ }
+
+ ret = iwl_mvm_disable_beacon_filter(mvm, vif);
+ if (ret)
+ goto out_release;
+
/*
* P2P_DEVICE interface does not have a channel context assigned to it,
* so a dedicated PHY context is allocated to it and the corresponding
* MAC context is bound to it at this stage.
*/
if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
- struct ieee80211_channel *chan;
- struct cfg80211_chan_def chandef;
- mvmvif->phy_ctxt = &mvm->phy_ctxt_roc;
-
- /*
- * The channel used here isn't relevant as it's
- * going to be overwritten as part of the ROC flow.
- * For now use the first channel we have.
- */
- chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0];
- cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
- ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt,
- &chandef, 1, 1);
- if (ret)
+ mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
+ if (!mvmvif->phy_ctxt) {
+ ret = -ENOSPC;
goto out_remove_mac;
+ }
+ iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
ret = iwl_mvm_binding_add_vif(mvm, vif);
if (ret)
- goto out_remove_phy;
+ goto out_unref_phy;
ret = iwl_mvm_add_bcast_sta(mvm, vif, &mvmvif->bcast_sta);
if (ret)
@@ -571,27 +603,17 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
out_unbind:
iwl_mvm_binding_remove_vif(mvm, vif);
- out_remove_phy:
- iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt);
+ out_unref_phy:
+ iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
out_remove_mac:
mvmvif->phy_ctxt = NULL;
iwl_mvm_mac_ctxt_remove(mvm, vif);
out_release:
- /*
- * TODO: remove this temporary code.
- * Currently MVM FW supports power management only on single MAC.
- * Check if only one additional interface remains after releasing
- * current one. Update power mode on the remaining interface.
- */
if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
mvm->vif_count--;
- IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
- mvm->vif_count);
- if (mvm->vif_count == 1) {
- ieee80211_iterate_active_interfaces(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_power_update_iterator, mvm);
- }
+ ieee80211_iterate_active_interfaces(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_power_update_iterator, mvm);
iwl_mvm_mac_ctxt_release(mvm, vif);
out_unlock:
mutex_unlock(&mvm->mutex);
@@ -646,6 +668,11 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
mutex_lock(&mvm->mutex);
+ if (mvm->bf_allowed_vif == mvmvif) {
+ mvm->bf_allowed_vif = NULL;
+ vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER;
+ }
+
iwl_mvm_vif_dbgfs_clean(mvm, vif);
/*
@@ -661,7 +688,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
mvm->p2p_device_vif = NULL;
iwl_mvm_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
iwl_mvm_binding_remove_vif(mvm, vif);
- iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt);
+ iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
mvmvif->phy_ctxt = NULL;
}
@@ -748,6 +775,9 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
if (ret)
IWL_ERR(mvm, "failed to update quotas\n");
}
+ ret = iwl_mvm_power_update_mode(mvm, vif);
+ if (ret)
+ IWL_ERR(mvm, "failed to update power mode\n");
} else if (changes & BSS_CHANGED_DTIM_PERIOD) {
/*
* We received a beacon _after_ association so
@@ -756,19 +786,9 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
iwl_mvm_remove_time_event(mvm, mvmvif,
&mvmvif->time_event_data);
} else if (changes & BSS_CHANGED_PS) {
- /*
- * TODO: remove this temporary code.
- * Currently MVM FW supports power management only on single
- * MAC. Avoid power mode update if more than one interface
- * is active.
- */
- IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
- mvm->vif_count);
- if (mvm->vif_count == 1) {
- ret = iwl_mvm_power_update_mode(mvm, vif);
- if (ret)
- IWL_ERR(mvm, "failed to update power mode\n");
- }
+ ret = iwl_mvm_power_update_mode(mvm, vif);
+ if (ret)
+ IWL_ERR(mvm, "failed to update power mode\n");
}
}
@@ -999,9 +1019,13 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
mvmvif->phy_ctxt->channel->band);
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTHORIZED) {
+ /* enable beacon filtering */
+ WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif));
ret = 0;
} else if (old_state == IEEE80211_STA_AUTHORIZED &&
new_state == IEEE80211_STA_ASSOC) {
+ /* disable beacon filtering */
+ WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif));
ret = 0;
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTH) {
@@ -1167,29 +1191,107 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
enum ieee80211_roc_type type)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct cfg80211_chan_def chandef;
- int ret;
+ struct iwl_mvm_phy_ctxt *phy_ctxt;
+ int ret, i;
+
+ IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
+ duration, type);
if (vif->type != NL80211_IFTYPE_P2P_DEVICE) {
IWL_ERR(mvm, "vif isn't a P2P_DEVICE: %d\n", vif->type);
return -EINVAL;
}
- IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
- duration, type);
-
mutex_lock(&mvm->mutex);
+ for (i = 0; i < NUM_PHY_CTX; i++) {
+ phy_ctxt = &mvm->phy_ctxts[i];
+ if (phy_ctxt->ref == 0 || mvmvif->phy_ctxt == phy_ctxt)
+ continue;
+
+ if (phy_ctxt->ref && channel == phy_ctxt->channel) {
+ /*
+ * Unbind the P2P_DEVICE from the current PHY context,
+ * and if the PHY context is not used remove it.
+ */
+ ret = iwl_mvm_binding_remove_vif(mvm, vif);
+ if (WARN(ret, "Failed unbinding P2P_DEVICE\n"))
+ goto out_unlock;
+
+ iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
+
+ /* Bind the P2P_DEVICE to the current PHY Context */
+ mvmvif->phy_ctxt = phy_ctxt;
+
+ ret = iwl_mvm_binding_add_vif(mvm, vif);
+ if (WARN(ret, "Failed binding P2P_DEVICE\n"))
+ goto out_unlock;
+
+ iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
+ goto schedule_time_event;
+ }
+ }
+
+ /* Need to update the PHY context only if the ROC channel changed */
+ if (channel == mvmvif->phy_ctxt->channel)
+ goto schedule_time_event;
+
cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT);
- ret = iwl_mvm_phy_ctxt_changed(mvm, &mvm->phy_ctxt_roc,
- &chandef, 1, 1);
+ /*
+ * Change the PHY context configuration as it is currently referenced
+ * only by the P2P Device MAC
+ */
+ if (mvmvif->phy_ctxt->ref == 1) {
+ ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt,
+ &chandef, 1, 1);
+ if (ret)
+ goto out_unlock;
+ } else {
+ /*
+ * The PHY context is shared with other MACs. Need to remove the
+ * P2P Device from the binding, allocate an new PHY context and
+ * create a new binding
+ */
+ phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
+ if (!phy_ctxt) {
+ ret = -ENOSPC;
+ goto out_unlock;
+ }
+
+ ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chandef,
+ 1, 1);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to change PHY context\n");
+ goto out_unlock;
+ }
+
+ /* Unbind the P2P_DEVICE from the current PHY context */
+ ret = iwl_mvm_binding_remove_vif(mvm, vif);
+ if (WARN(ret, "Failed unbinding P2P_DEVICE\n"))
+ goto out_unlock;
+
+ iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
+
+ /* Bind the P2P_DEVICE to the new allocated PHY context */
+ mvmvif->phy_ctxt = phy_ctxt;
+
+ ret = iwl_mvm_binding_add_vif(mvm, vif);
+ if (WARN(ret, "Failed binding P2P_DEVICE\n"))
+ goto out_unlock;
+
+ iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
+ }
+
+schedule_time_event:
/* Schedule the time events */
ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type);
+out_unlock:
mutex_unlock(&mvm->mutex);
IWL_DEBUG_MAC80211(mvm, "leave\n");
-
return ret;
}
@@ -1211,15 +1313,30 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+ u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+ struct iwl_mvm_phy_ctxt *phy_ctxt;
int ret;
+ IWL_DEBUG_MAC80211(mvm, "Add channel context\n");
+
mutex_lock(&mvm->mutex);
+ phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
+ if (!phy_ctxt) {
+ ret = -ENOSPC;
+ goto out;
+ }
- IWL_DEBUG_MAC80211(mvm, "Add PHY context\n");
- ret = iwl_mvm_phy_ctxt_add(mvm, phy_ctxt, &ctx->def,
- ctx->rx_chains_static,
- ctx->rx_chains_dynamic);
+ ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
+ ctx->rx_chains_static,
+ ctx->rx_chains_dynamic);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to add PHY context\n");
+ goto out;
+ }
+
+ iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt);
+ *phy_ctxt_id = phy_ctxt->id;
+out:
mutex_unlock(&mvm->mutex);
return ret;
}
@@ -1228,10 +1345,11 @@ static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+ u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+ struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
mutex_lock(&mvm->mutex);
- iwl_mvm_phy_ctxt_remove(mvm, phy_ctxt);
+ iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt);
mutex_unlock(&mvm->mutex);
}
@@ -1240,7 +1358,16 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
u32 changed)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+ u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+ struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
+
+ if (WARN_ONCE((phy_ctxt->ref > 1) &&
+ (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH |
+ IEEE80211_CHANCTX_CHANGE_RX_CHAINS |
+ IEEE80211_CHANCTX_CHANGE_RADAR)),
+ "Cannot change PHY. Ref=%d, changed=0x%X\n",
+ phy_ctxt->ref, changed))
+ return;
mutex_lock(&mvm->mutex);
iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
@@ -1254,13 +1381,14 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_phy_ctxt *phyctx = (void *)ctx->drv_priv;
+ u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+ struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
int ret;
mutex_lock(&mvm->mutex);
- mvmvif->phy_ctxt = phyctx;
+ mvmvif->phy_ctxt = phy_ctxt;
switch (vif->type) {
case NL80211_IFTYPE_AP:
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index 9f46b23801bc..4e10aae71038 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -88,6 +88,7 @@ enum iwl_mvm_tx_fifo {
IWL_MVM_TX_FIFO_BE,
IWL_MVM_TX_FIFO_VI,
IWL_MVM_TX_FIFO_VO,
+ IWL_MVM_TX_FIFO_MCAST = 5,
};
extern struct ieee80211_ops iwl_mvm_hw_ops;
@@ -109,6 +110,7 @@ extern struct iwl_mvm_mod_params iwlmvm_mod_params;
struct iwl_mvm_phy_ctxt {
u16 id;
u16 color;
+ u32 ref;
/*
* TODO: This should probably be removed. Currently here only for rate
@@ -149,6 +151,60 @@ enum iwl_power_scheme {
#define IWL_CONN_MAX_LISTEN_INTERVAL 70
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+enum iwl_dbgfs_pm_mask {
+ MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0),
+ MVM_DEBUGFS_PM_SKIP_OVER_DTIM = BIT(1),
+ MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS = BIT(2),
+ MVM_DEBUGFS_PM_RX_DATA_TIMEOUT = BIT(3),
+ MVM_DEBUGFS_PM_TX_DATA_TIMEOUT = BIT(4),
+ MVM_DEBUGFS_PM_DISABLE_POWER_OFF = BIT(5),
+};
+
+struct iwl_dbgfs_pm {
+ u8 keep_alive_seconds;
+ u32 rx_data_timeout;
+ u32 tx_data_timeout;
+ bool skip_over_dtim;
+ u8 skip_dtim_periods;
+ bool disable_power_off;
+ int mask;
+};
+
+/* beacon filtering */
+
+enum iwl_dbgfs_bf_mask {
+ MVM_DEBUGFS_BF_ENERGY_DELTA = BIT(0),
+ MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA = BIT(1),
+ MVM_DEBUGFS_BF_ROAMING_STATE = BIT(2),
+ MVM_DEBUGFS_BF_TEMPERATURE_DELTA = BIT(3),
+ MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(4),
+ MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(5),
+ MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(6),
+ MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(7),
+ MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(8),
+};
+
+struct iwl_dbgfs_bf {
+ u8 bf_energy_delta;
+ u8 bf_roaming_energy_delta;
+ u8 bf_roaming_state;
+ u8 bf_temperature_delta;
+ u8 bf_enable_beacon_filter;
+ u8 bf_debug_flag;
+ u32 bf_escape_timer;
+ u32 ba_escape_timer;
+ u8 ba_enable_beacon_abort;
+ int mask;
+};
+#endif
+
+enum iwl_mvm_smps_type_request {
+ IWL_MVM_SMPS_REQ_BT_COEX,
+ IWL_MVM_SMPS_REQ_TT,
+ NUM_IWL_MVM_SMPS_REQ,
+};
+
/**
* struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context
* @id: between 0 and 3
@@ -163,6 +219,8 @@ enum iwl_power_scheme {
* @bcast_sta: station used for broadcast packets. Used by the following
* vifs: P2P_DEVICE, GO and AP.
* @beacon_skb: the skb used to hold the AP/GO beacon template
+ * @smps_requests: the requests of of differents parts of the driver, regard
+ the desired smps mode.
*/
struct iwl_mvm_vif {
u16 id;
@@ -172,6 +230,8 @@ struct iwl_mvm_vif {
bool uploaded;
bool ap_active;
bool monitor_active;
+ /* indicate whether beacon filtering is enabled */
+ bool bf_enabled;
u32 ap_beacon_time;
@@ -214,7 +274,11 @@ struct iwl_mvm_vif {
struct dentry *dbgfs_dir;
struct dentry *dbgfs_slink;
void *dbgfs_data;
+ struct iwl_dbgfs_pm dbgfs_pm;
+ struct iwl_dbgfs_bf dbgfs_bf;
#endif
+
+ enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ];
};
static inline struct iwl_mvm_vif *
@@ -223,12 +287,6 @@ iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif)
return (void *)vif->drv_priv;
}
-enum iwl_mvm_status {
- IWL_MVM_STATUS_HW_RFKILL,
- IWL_MVM_STATUS_ROC_RUNNING,
- IWL_MVM_STATUS_IN_HW_RESTART,
-};
-
enum iwl_scan_status {
IWL_MVM_SCAN_NONE,
IWL_MVM_SCAN_OS,
@@ -246,6 +304,63 @@ struct iwl_nvm_section {
const u8 *data;
};
+/*
+ * Tx-backoff threshold
+ * @temperature: The threshold in Celsius
+ * @backoff: The tx-backoff in uSec
+ */
+struct iwl_tt_tx_backoff {
+ s32 temperature;
+ u32 backoff;
+};
+
+#define TT_TX_BACKOFF_SIZE 6
+
+/**
+ * struct iwl_tt_params - thermal throttling parameters
+ * @ct_kill_entry: CT Kill entry threshold
+ * @ct_kill_exit: CT Kill exit threshold
+ * @ct_kill_duration: The time intervals (in uSec) in which the driver needs
+ * to checks whether to exit CT Kill.
+ * @dynamic_smps_entry: Dynamic SMPS entry threshold
+ * @dynamic_smps_exit: Dynamic SMPS exit threshold
+ * @tx_protection_entry: TX protection entry threshold
+ * @tx_protection_exit: TX protection exit threshold
+ * @tx_backoff: Array of thresholds for tx-backoff , in ascending order.
+ * @support_ct_kill: Support CT Kill?
+ * @support_dynamic_smps: Support dynamic SMPS?
+ * @support_tx_protection: Support tx protection?
+ * @support_tx_backoff: Support tx-backoff?
+ */
+struct iwl_tt_params {
+ s32 ct_kill_entry;
+ s32 ct_kill_exit;
+ u32 ct_kill_duration;
+ s32 dynamic_smps_entry;
+ s32 dynamic_smps_exit;
+ s32 tx_protection_entry;
+ s32 tx_protection_exit;
+ struct iwl_tt_tx_backoff tx_backoff[TT_TX_BACKOFF_SIZE];
+ bool support_ct_kill;
+ bool support_dynamic_smps;
+ bool support_tx_protection;
+ bool support_tx_backoff;
+};
+
+/**
+ * struct iwl_mvm_tt_mgnt - Thermal Throttling Management structure
+ * @ct_kill_exit: worker to exit thermal kill
+ * @dynamic_smps: Is thermal throttling enabled dynamic_smps?
+ * @tx_backoff: The current thremal throttling tx backoff in uSec.
+ * @params: Parameters to configure the thermal throttling algorithm.
+ */
+struct iwl_mvm_tt_mgmt {
+ struct delayed_work ct_kill_exit;
+ bool dynamic_smps;
+ u32 tx_backoff;
+ const struct iwl_tt_params *params;
+};
+
struct iwl_mvm {
/* for logger access */
struct device *dev;
@@ -266,6 +381,12 @@ struct iwl_mvm {
unsigned long status;
+ /*
+ * for beacon filtering -
+ * currently only one interface can be supported
+ */
+ struct iwl_mvm_vif *bf_allowed_vif;
+
enum iwl_ucode_type cur_ucode;
bool ucode_loaded;
bool init_ucode_run;
@@ -313,7 +434,7 @@ struct iwl_mvm {
bool prevent_power_down_d3;
#endif
- struct iwl_mvm_phy_ctxt phy_ctxt_roc;
+ struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX];
struct list_head time_event_list;
spinlock_t time_event_lock;
@@ -338,11 +459,21 @@ struct iwl_mvm {
#ifdef CONFIG_PM_SLEEP
int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ bool d3_test_active;
+ bool store_d3_resume_sram;
+ void *d3_resume_sram;
+ u32 d3_test_pme_ptr;
+#endif
#endif
/* BT-Coex */
u8 bt_kill_msk;
struct iwl_bt_coex_profile_notif last_bt_notif;
+
+ /* Thermal Throttling and CTkill */
+ struct iwl_mvm_tt_mgmt thermal_throttle;
+ s32 temperature; /* Celsius */
};
/* Extract MVM priv from op_mode and _hw */
@@ -352,6 +483,19 @@ struct iwl_mvm {
#define IWL_MAC80211_GET_MVM(_hw) \
IWL_OP_MODE_GET_MVM((struct iwl_op_mode *)((_hw)->priv))
+enum iwl_mvm_status {
+ IWL_MVM_STATUS_HW_RFKILL,
+ IWL_MVM_STATUS_HW_CTKILL,
+ IWL_MVM_STATUS_ROC_RUNNING,
+ IWL_MVM_STATUS_IN_HW_RESTART,
+};
+
+static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
+{
+ return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status) ||
+ test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
+}
+
extern const u8 iwl_mvm_ac_to_tx_fifo[];
struct iwl_rate_info {
@@ -443,8 +587,10 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
struct cfg80211_chan_def *chandef,
u8 chains_static, u8 chains_dynamic);
-void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm,
- struct iwl_mvm_phy_ctxt *ctxt);
+void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm,
+ struct iwl_mvm_phy_ctxt *ctxt);
+void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm,
+ struct iwl_mvm_phy_ctxt *ctxt);
/* MAC (virtual interface) programming */
int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -459,6 +605,9 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd);
/* Bindings */
int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -523,6 +672,7 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
struct inet6_dev *idev);
void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, int idx);
+extern const struct file_operations iwl_dbgfs_d3_test_ops;
/* BT Coex */
int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm);
@@ -534,4 +684,36 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
enum ieee80211_rssi_event rssi_event);
void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+/* beacon filtering */
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+void
+iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
+ struct iwl_beacon_filter_cmd *cmd);
+int iwl_mvm_dbgfs_set_fw_dbg_log(struct iwl_mvm *mvm);
+#else
+static inline void
+iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
+ struct iwl_beacon_filter_cmd *cmd)
+{}
+static inline int iwl_mvm_dbgfs_set_fw_dbg_log(struct iwl_mvm *mvm)
+{
+ return 0;
+}
+#endif
+int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif);
+int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif);
+
+/* SMPS */
+void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ enum iwl_mvm_smps_type_request req_type,
+ enum ieee80211_smps_mode smps_request);
+
+/* Thermal management and CT-kill */
+void iwl_mvm_tt_handler(struct iwl_mvm *mvm);
+void iwl_mvm_tt_initialize(struct iwl_mvm *mvm);
+void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
+void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
+
#endif /* __IWL_MVM_H__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c
index b8ec02f89acc..edb94ea31654 100644
--- a/drivers/net/wireless/iwlwifi/mvm/nvm.c
+++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c
@@ -60,6 +60,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
+#include <linux/firmware.h>
#include "iwl-trans.h"
#include "mvm.h"
#include "iwl-eeprom-parse.h"
@@ -75,31 +76,56 @@ static const int nvm_to_read[] = {
};
/* Default NVM size to read */
-#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024);
+#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024)
+#define IWL_MAX_NVM_SECTION_SIZE 6000
-static inline void iwl_nvm_fill_read(struct iwl_nvm_access_cmd *cmd,
- u16 offset, u16 length, u16 section)
+#define NVM_WRITE_OPCODE 1
+#define NVM_READ_OPCODE 0
+
+/*
+ * prepare the NVM host command w/ the pointers to the nvm buffer
+ * and send it to fw
+ */
+static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section,
+ u16 offset, u16 length, const u8 *data)
{
- cmd->offset = cpu_to_le16(offset);
- cmd->length = cpu_to_le16(length);
- cmd->type = cpu_to_le16(section);
+ struct iwl_nvm_access_cmd nvm_access_cmd = {
+ .offset = cpu_to_le16(offset),
+ .length = cpu_to_le16(length),
+ .type = cpu_to_le16(section),
+ .op_code = NVM_WRITE_OPCODE,
+ };
+ struct iwl_host_cmd cmd = {
+ .id = NVM_ACCESS_CMD,
+ .len = { sizeof(struct iwl_nvm_access_cmd), length },
+ .flags = CMD_SYNC | CMD_SEND_IN_RFKILL,
+ .data = { &nvm_access_cmd, data },
+ /* data may come from vmalloc, so use _DUP */
+ .dataflags = { 0, IWL_HCMD_DFL_DUP },
+ };
+
+ return iwl_mvm_send_cmd(mvm, &cmd);
}
static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
u16 offset, u16 length, u8 *data)
{
- struct iwl_nvm_access_cmd nvm_access_cmd = {};
+ struct iwl_nvm_access_cmd nvm_access_cmd = {
+ .offset = cpu_to_le16(offset),
+ .length = cpu_to_le16(length),
+ .type = cpu_to_le16(section),
+ .op_code = NVM_READ_OPCODE,
+ };
struct iwl_nvm_access_resp *nvm_resp;
struct iwl_rx_packet *pkt;
struct iwl_host_cmd cmd = {
.id = NVM_ACCESS_CMD,
- .flags = CMD_SYNC | CMD_WANT_SKB,
+ .flags = CMD_SYNC | CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
.data = { &nvm_access_cmd, },
};
int ret, bytes_read, offset_read;
u8 *resp_data;
- iwl_nvm_fill_read(&nvm_access_cmd, offset, length, section);
cmd.len[0] = sizeof(struct iwl_nvm_access_cmd);
ret = iwl_mvm_send_cmd(mvm, &cmd);
@@ -144,6 +170,30 @@ exit:
return ret;
}
+static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section,
+ const u8 *data, u16 length)
+{
+ int offset = 0;
+
+ /* copy data in chunks of 2k (and remainder if any) */
+
+ while (offset < length) {
+ int chunk_size, ret;
+
+ chunk_size = min(IWL_NVM_DEFAULT_CHUNK_SIZE,
+ length - offset);
+
+ ret = iwl_nvm_write_chunk(mvm, section, offset,
+ chunk_size, data + offset);
+ if (ret < 0)
+ return ret;
+
+ offset += chunk_size;
+ }
+
+ return 0;
+}
+
/*
* Reads an NVM section completely.
* NICs prior to 7000 family doesn't have a real NVM, but just read
@@ -177,7 +227,8 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
offset += ret;
}
- IWL_INFO(mvm, "NVM section %d read completed\n", section);
+ IWL_DEBUG_EEPROM(mvm->trans->dev,
+ "NVM section %d read completed\n", section);
return offset;
}
@@ -200,7 +251,130 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
hw = (const __le16 *)sections[NVM_SECTION_TYPE_HW].data;
sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;
calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data;
- return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib);
+ return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib,
+ iwl_fw_valid_tx_ant(mvm->fw),
+ iwl_fw_valid_rx_ant(mvm->fw));
+}
+
+#define MAX_NVM_FILE_LEN 16384
+
+/*
+ * HOW TO CREATE THE NVM FILE FORMAT:
+ * ------------------------------
+ * 1. create hex file, format:
+ * 3800 -> header
+ * 0000 -> header
+ * 5a40 -> data
+ *
+ * rev - 6 bit (word1)
+ * len - 10 bit (word1)
+ * id - 4 bit (word2)
+ * rsv - 12 bit (word2)
+ *
+ * 2. flip 8bits with 8 bits per line to get the right NVM file format
+ *
+ * 3. create binary file from the hex file
+ *
+ * 4. save as "iNVM_xxx.bin" under /lib/firmware
+ */
+static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm)
+{
+ int ret, section_id, section_size;
+ const struct firmware *fw_entry;
+ const struct {
+ __le16 word1;
+ __le16 word2;
+ u8 data[];
+ } *file_sec;
+ const u8 *eof;
+
+#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF))
+#define NVM_WORD2_ID(x) (x >> 12)
+
+ /*
+ * Obtain NVM image via request_firmware. Since we already used
+ * request_firmware_nowait() for the firmware binary load and only
+ * get here after that we assume the NVM request can be satisfied
+ * synchronously.
+ */
+ ret = request_firmware(&fw_entry, iwlwifi_mod_params.nvm_file,
+ mvm->trans->dev);
+ if (ret) {
+ IWL_ERR(mvm, "ERROR: %s isn't available %d\n",
+ iwlwifi_mod_params.nvm_file, ret);
+ return ret;
+ }
+
+ IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n",
+ iwlwifi_mod_params.nvm_file, fw_entry->size);
+
+ if (fw_entry->size < sizeof(*file_sec)) {
+ IWL_ERR(mvm, "NVM file too small\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (fw_entry->size > MAX_NVM_FILE_LEN) {
+ IWL_ERR(mvm, "NVM file too large\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ eof = fw_entry->data + fw_entry->size;
+
+ file_sec = (void *)fw_entry->data;
+
+ while (true) {
+ if (file_sec->data > eof) {
+ IWL_ERR(mvm,
+ "ERROR - NVM file too short for section header\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ /* check for EOF marker */
+ if (!file_sec->word1 && !file_sec->word2) {
+ ret = 0;
+ break;
+ }
+
+ section_size = 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1));
+ section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2));
+
+ if (section_size > IWL_MAX_NVM_SECTION_SIZE) {
+ IWL_ERR(mvm, "ERROR - section too large (%d)\n",
+ section_size);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (!section_size) {
+ IWL_ERR(mvm, "ERROR - section empty\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ if (file_sec->data + section_size > eof) {
+ IWL_ERR(mvm,
+ "ERROR - NVM file too short for section (%d bytes)\n",
+ section_size);
+ ret = -EINVAL;
+ break;
+ }
+
+ ret = iwl_nvm_write_section(mvm, section_id, file_sec->data,
+ section_size);
+ if (ret < 0) {
+ IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret);
+ break;
+ }
+
+ /* advance to the next section */
+ file_sec = (void *)(file_sec->data + section_size);
+ }
+out:
+ release_firmware(fw_entry);
+ return ret;
}
int iwl_nvm_init(struct iwl_mvm *mvm)
@@ -208,6 +382,17 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
int ret, i, section;
u8 *nvm_buffer, *temp;
+ /* load external NVM if configured */
+ if (iwlwifi_mod_params.nvm_file) {
+ /* move to External NVM flow */
+ ret = iwl_mvm_load_external_nvm(mvm);
+ if (ret)
+ return ret;
+ }
+
+ /* Read From FW NVM */
+ IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n");
+
/* TODO: find correct NVM max size for a section */
nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
GFP_KERNEL);
@@ -231,8 +416,9 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
if (ret < 0)
return ret;
- ret = 0;
mvm->nvm_data = iwl_parse_nvm_sections(mvm);
+ if (!mvm->nvm_data)
+ return -ENODATA;
- return ret;
+ return 0;
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index b29c31a41594..af79a14063a9 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -215,17 +215,22 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
RX_HANDLER(REPLY_RX_PHY_CMD, iwl_mvm_rx_rx_phy_cmd, false),
RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, false),
RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false),
+
+ RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true),
+ RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false),
+ RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true),
+
RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false),
RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false),
RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false),
- RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true),
- RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false),
-
RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false),
RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false),
+ RX_HANDLER(MISSED_BEACONS_NOTIFICATION, iwl_mvm_rx_missed_beacons_notif,
+ false),
+
RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false),
};
#undef RX_HANDLER
@@ -288,11 +293,14 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
CMD(NET_DETECT_HOTSPOTS_CMD),
CMD(NET_DETECT_HOTSPOTS_QUERY_CMD),
CMD(CARD_STATE_NOTIFICATION),
+ CMD(MISSED_BEACONS_NOTIFICATION),
CMD(BT_COEX_PRIO_TABLE),
CMD(BT_COEX_PROT_ENV),
CMD(BT_PROFILE_NOTIFICATION),
CMD(BT_CONFIG),
CMD(MCAST_FILTER_CMD),
+ CMD(REPLY_BEACON_FILTERING_CMD),
+ CMD(REPLY_THERMAL_MNG_BACKOFF),
};
#undef CMD
@@ -393,10 +401,13 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
if (err)
goto out_free;
+ iwl_mvm_tt_initialize(mvm);
+
mutex_lock(&mvm->mutex);
err = iwl_run_init_mvm_ucode(mvm, true);
mutex_unlock(&mvm->mutex);
- if (err && !iwlmvm_mod_params.init_dbg) {
+ /* returns 0 if successful, 1 if success but in rfkill */
+ if (err < 0 && !iwlmvm_mod_params.init_dbg) {
IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err);
goto out_free;
}
@@ -439,10 +450,16 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
iwl_mvm_leds_exit(mvm);
+ iwl_mvm_tt_exit(mvm);
+
ieee80211_unregister_hw(mvm->hw);
kfree(mvm->scan_cmd);
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS)
+ kfree(mvm->d3_resume_sram);
+#endif
+
iwl_trans_stop_hw(mvm->trans, true);
iwl_phy_db_free(mvm->phy_db);
@@ -589,6 +606,16 @@ static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue)
ieee80211_wake_queue(mvm->hw, mq);
}
+void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state)
+{
+ if (state)
+ set_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
+ else
+ clear_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
+
+ wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
+}
+
static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
@@ -598,7 +625,7 @@ static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
else
clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
- wiphy_rfkill_set_hw_state(mvm->hw->wiphy, state);
+ wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
}
static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
index a28a1d1f23eb..a8652ddd6bed 100644
--- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
@@ -195,21 +195,6 @@ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
return ret;
}
-
-struct phy_ctx_used_data {
- unsigned long used[BITS_TO_LONGS(NUM_PHY_CTX)];
-};
-
-static void iwl_mvm_phy_ctx_used_iter(struct ieee80211_hw *hw,
- struct ieee80211_chanctx_conf *ctx,
- void *_data)
-{
- struct phy_ctx_used_data *data = _data;
- struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
-
- __set_bit(phy_ctxt->id, data->used);
-}
-
/*
* Send a command to add a PHY context based on the current HW configuration.
*/
@@ -217,34 +202,28 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
struct cfg80211_chan_def *chandef,
u8 chains_static, u8 chains_dynamic)
{
- struct phy_ctx_used_data data = {
- .used = { },
- };
-
- /*
- * If this is a regular PHY context (not the ROC one)
- * skip the ROC PHY context's ID.
- */
- if (ctxt != &mvm->phy_ctxt_roc)
- __set_bit(mvm->phy_ctxt_roc.id, data.used);
+ int ret;
+ WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
+ ctxt->ref);
lockdep_assert_held(&mvm->mutex);
- ctxt->color++;
- if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
- ieee80211_iter_chan_contexts_atomic(
- mvm->hw, iwl_mvm_phy_ctx_used_iter, &data);
+ ctxt->channel = chandef->chan;
+ ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
+ chains_static, chains_dynamic,
+ FW_CTXT_ACTION_ADD, 0);
- ctxt->id = find_first_zero_bit(data.used, NUM_PHY_CTX);
- if (WARN_ONCE(ctxt->id == NUM_PHY_CTX,
- "Failed to init PHY context - no free ID!\n"))
- return -EIO;
- }
+ return ret;
+}
- ctxt->channel = chandef->chan;
- return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
- chains_static, chains_dynamic,
- FW_CTXT_ACTION_ADD, 0);
+/*
+ * Update the number of references to the given PHY context. This is valid only
+ * in case the PHY context was already created, i.e., its reference count > 0.
+ */
+void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
+{
+ lockdep_assert_held(&mvm->mutex);
+ ctxt->ref++;
}
/*
@@ -264,23 +243,12 @@ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
FW_CTXT_ACTION_MODIFY, 0);
}
-/*
- * Send a command to the FW to remove the given phy context.
- * Once the command is sent, regardless of success or failure, the context is
- * marked as invalid
- */
-void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
+void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
{
- struct iwl_phy_context_cmd cmd;
- int ret;
-
lockdep_assert_held(&mvm->mutex);
- iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, FW_CTXT_ACTION_REMOVE, 0);
- ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC,
- sizeof(struct iwl_phy_context_cmd),
- &cmd);
- if (ret)
- IWL_ERR(mvm, "Failed to send PHY remove: ctxt id=%d\n",
- ctxt->id);
+ if (WARN_ON_ONCE(!ctxt))
+ return;
+
+ ctxt->ref--;
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c
index ed77e437aac4..3760a33ca3a4 100644
--- a/drivers/net/wireless/iwlwifi/mvm/power.c
+++ b/drivers/net/wireless/iwlwifi/mvm/power.c
@@ -75,6 +75,54 @@
#define POWER_KEEP_ALIVE_PERIOD_SEC 25
+static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
+ struct iwl_beacon_filter_cmd *cmd)
+{
+ int ret;
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, CMD_SYNC,
+ sizeof(struct iwl_beacon_filter_cmd), cmd);
+
+ if (!ret) {
+ IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
+ cmd->ba_enable_beacon_abort);
+ IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
+ cmd->ba_escape_timer);
+ IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
+ cmd->bf_debug_flag);
+ IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
+ cmd->bf_enable_beacon_filter);
+ IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
+ cmd->bf_energy_delta);
+ IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
+ cmd->bf_escape_timer);
+ IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
+ cmd->bf_roaming_energy_delta);
+ IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
+ cmd->bf_roaming_state);
+ IWL_DEBUG_POWER(mvm, "bf_temperature_delta is: %d\n",
+ cmd->bf_temperature_delta);
+ }
+ return ret;
+}
+
+static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif, bool enable)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_beacon_filter_cmd cmd = {
+ IWL_BF_CMD_CONFIG_DEFAULTS,
+ .bf_enable_beacon_filter = 1,
+ .ba_enable_beacon_abort = enable,
+ };
+
+ if (!mvmvif->bf_enabled)
+ return 0;
+
+ iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
+ return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
+}
+
static void iwl_mvm_power_log(struct iwl_mvm *mvm,
struct iwl_powertable_cmd *cmd)
{
@@ -91,6 +139,9 @@ static void iwl_mvm_power_log(struct iwl_mvm *mvm,
le32_to_cpu(cmd->tx_data_timeout));
IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
cmd->lprx_rssi_threshold);
+ if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))
+ IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",
+ le32_to_cpu(cmd->skip_dtim_periods));
}
}
@@ -103,6 +154,8 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
int dtimper, dtimper_msec;
int keep_alive;
bool radar_detect = false;
+ struct iwl_mvm_vif *mvmvif __maybe_unused =
+ iwl_mvm_vif_from_mac80211(vif);
/*
* Regardless of power management state the driver must set
@@ -115,7 +168,14 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
return;
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
+ if (!vif->bss_conf.assoc)
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
+ mvmvif->dbgfs_pm.disable_power_off)
+ cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
+#endif
if (!vif->bss_conf.ps)
return;
@@ -135,8 +195,11 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
/* Check skip over DTIM conditions */
if (!radar_detect && (dtimper <= 10) &&
- (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP))
+ (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
+ mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+ cmd->skip_dtim_periods = cpu_to_le32(3);
+ }
/* Check that keep alive period is at least 3 * DTIM */
dtimper_msec = dtimper * vif->bss_conf.beacon_int;
@@ -145,27 +208,76 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
cmd->keep_alive_seconds = keep_alive;
- cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
- cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
+ if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
+ cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
+ cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
+ } else {
+ cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
+ cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
+ }
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
+ cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds;
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
+ if (mvmvif->dbgfs_pm.skip_over_dtim)
+ cmd->flags |=
+ cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+ else
+ cmd->flags &=
+ cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+ }
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT)
+ cmd->rx_data_timeout =
+ cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout);
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT)
+ cmd->tx_data_timeout =
+ cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
+ cmd->skip_dtim_periods =
+ cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods);
+#endif /* CONFIG_IWLWIFI_DEBUGFS */
}
int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
+ int ret;
+ bool ba_enable;
struct iwl_powertable_cmd cmd = {};
if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
return 0;
+ /*
+ * TODO: The following vif_count verification is temporary condition.
+ * Avoid power mode update if more than one interface is currently
+ * active. Remove this condition when FW will support power management
+ * on multiple MACs.
+ */
+ IWL_DEBUG_POWER(mvm, "Currently %d interfaces active\n",
+ mvm->vif_count);
+ if (mvm->vif_count > 1)
+ return 0;
+
iwl_mvm_power_build_cmd(mvm, vif, &cmd);
iwl_mvm_power_log(mvm, &cmd);
- return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
- sizeof(cmd), &cmd);
+ ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
+ sizeof(cmd), &cmd);
+ if (ret)
+ return ret;
+
+ ba_enable = !!(cmd.flags &
+ cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK));
+
+ return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
}
int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_powertable_cmd cmd = {};
+ struct iwl_mvm_vif *mvmvif __maybe_unused =
+ iwl_mvm_vif_from_mac80211(vif);
if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
return 0;
@@ -173,8 +285,82 @@ int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
+ mvmvif->dbgfs_pm.disable_power_off)
+ cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
+#endif
iwl_mvm_power_log(mvm, &cmd);
return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC,
sizeof(cmd), &cmd);
}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+void
+iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
+ struct iwl_beacon_filter_cmd *cmd)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
+
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA)
+ cmd->bf_energy_delta = dbgfs_bf->bf_energy_delta;
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA)
+ cmd->bf_roaming_energy_delta =
+ dbgfs_bf->bf_roaming_energy_delta;
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE)
+ cmd->bf_roaming_state = dbgfs_bf->bf_roaming_state;
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMPERATURE_DELTA)
+ cmd->bf_temperature_delta = dbgfs_bf->bf_temperature_delta;
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG)
+ cmd->bf_debug_flag = dbgfs_bf->bf_debug_flag;
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER)
+ cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer);
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER)
+ cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer);
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT)
+ cmd->ba_enable_beacon_abort = dbgfs_bf->ba_enable_beacon_abort;
+}
+#endif
+
+int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_beacon_filter_cmd cmd = {
+ IWL_BF_CMD_CONFIG_DEFAULTS,
+ .bf_enable_beacon_filter = 1,
+ };
+ int ret;
+
+ if (mvmvif != mvm->bf_allowed_vif ||
+ vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+ return 0;
+
+ iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
+ ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
+
+ if (!ret)
+ mvmvif->bf_enabled = true;
+
+ return ret;
+}
+
+int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_beacon_filter_cmd cmd = {};
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+ return 0;
+
+ ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
+
+ if (!ret)
+ mvmvif->bf_enabled = false;
+
+ return ret;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c
index a1e3e923ea3e..29d49cf0fdb2 100644
--- a/drivers/net/wireless/iwlwifi/mvm/quota.c
+++ b/drivers/net/wireless/iwlwifi/mvm/quota.c
@@ -169,27 +169,34 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
num_active_bindings++;
}
- if (!num_active_bindings)
- goto send_cmd;
-
- quota = IWL_MVM_MAX_QUOTA / num_active_bindings;
- quota_rem = IWL_MVM_MAX_QUOTA % num_active_bindings;
+ quota = 0;
+ quota_rem = 0;
+ if (num_active_bindings) {
+ quota = IWL_MVM_MAX_QUOTA / num_active_bindings;
+ quota_rem = IWL_MVM_MAX_QUOTA % num_active_bindings;
+ }
for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
- if (data.n_interfaces[i] <= 0)
+ if (data.colors[i] < 0)
continue;
cmd.quotas[idx].id_and_color =
cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i]));
- cmd.quotas[idx].quota = cpu_to_le32(quota);
- cmd.quotas[idx].max_duration = cpu_to_le32(IWL_MVM_MAX_QUOTA);
+
+ if (data.n_interfaces[i] <= 0) {
+ cmd.quotas[idx].quota = cpu_to_le32(0);
+ cmd.quotas[idx].max_duration = cpu_to_le32(0);
+ } else {
+ cmd.quotas[idx].quota = cpu_to_le32(quota);
+ cmd.quotas[idx].max_duration =
+ cpu_to_le32(IWL_MVM_MAX_QUOTA);
+ }
idx++;
}
/* Give the remainder of the session to the first binding */
le32_add_cpu(&cmd.quotas[0].quota, quota_rem);
-send_cmd:
ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC,
sizeof(cmd), &cmd);
if (ret)
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c
index 55334d542e26..d6beec765d65 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.c
@@ -401,6 +401,17 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm,
load = rs_tl_get_load(lq_data, tid);
+ /*
+ * Don't create TX aggregation sessions when in high
+ * BT traffic, as they would just be disrupted by BT.
+ */
+ if (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= 2) {
+ IWL_DEBUG_COEX(mvm, "BT traffic (%d), no aggregation allowed\n",
+ BT_MBOX_MSG(&mvm->last_bt_notif,
+ 3, TRAFFIC_LOAD));
+ return ret;
+ }
+
if ((iwlwifi_mod_params.auto_agg) || (load > IWL_AGG_LOAD_THRESHOLD)) {
IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n",
sta->addr, tid);
@@ -1519,6 +1530,29 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
u8 update_search_tbl_counter = 0;
int ret;
+ switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
+ case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
+ /* nothing */
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
+ /* avoid antenna B unless MIMO */
+ if (tbl->action == IWL_SISO_SWITCH_ANTENNA2)
+ tbl->action = IWL_SISO_SWITCH_MIMO2_AB;
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
+ case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
+ /* avoid antenna B and MIMO */
+ valid_tx_ant =
+ first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
+ if (tbl->action != IWL_SISO_SWITCH_ANTENNA1)
+ tbl->action = IWL_SISO_SWITCH_ANTENNA1;
+ break;
+ default:
+ IWL_ERR(mvm, "Invalid BT load %d",
+ BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD));
+ break;
+ }
+
start_action = tbl->action;
while (1) {
lq_sta->action_counter++;
@@ -1532,7 +1566,9 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
tx_chains_num <= 2))
break;
- if (window->success_ratio >= IWL_RS_GOOD_RATIO)
+ if (window->success_ratio >= IWL_RS_GOOD_RATIO &&
+ BT_MBOX_MSG(&mvm->last_bt_notif, 3,
+ TRAFFIC_LOAD) == 0)
break;
memcpy(search_tbl, tbl, sz);
@@ -1654,6 +1690,28 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
u8 update_search_tbl_counter = 0;
int ret;
+ switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
+ case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
+ /* nothing */
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
+ case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
+ /* avoid antenna B and MIMO */
+ if (tbl->action != IWL_MIMO2_SWITCH_SISO_A)
+ tbl->action = IWL_MIMO2_SWITCH_SISO_A;
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
+ /* avoid antenna B unless MIMO */
+ if (tbl->action == IWL_MIMO2_SWITCH_SISO_B ||
+ tbl->action == IWL_MIMO2_SWITCH_SISO_C)
+ tbl->action = IWL_MIMO2_SWITCH_SISO_A;
+ break;
+ default:
+ IWL_ERR(mvm, "Invalid BT load %d",
+ BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD));
+ break;
+ }
+
start_action = tbl->action;
while (1) {
lq_sta->action_counter++;
@@ -1791,6 +1849,28 @@ static int rs_move_mimo3_to_other(struct iwl_mvm *mvm,
int ret;
u8 update_search_tbl_counter = 0;
+ switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
+ case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
+ /* nothing */
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
+ case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
+ /* avoid antenna B and MIMO */
+ if (tbl->action != IWL_MIMO3_SWITCH_SISO_A)
+ tbl->action = IWL_MIMO3_SWITCH_SISO_A;
+ break;
+ case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
+ /* avoid antenna B unless MIMO */
+ if (tbl->action == IWL_MIMO3_SWITCH_SISO_B ||
+ tbl->action == IWL_MIMO3_SWITCH_SISO_C)
+ tbl->action = IWL_MIMO3_SWITCH_SISO_A;
+ break;
+ default:
+ IWL_ERR(mvm, "Invalid BT load %d",
+ BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD));
+ break;
+ }
+
start_action = tbl->action;
while (1) {
lq_sta->action_counter++;
@@ -2302,6 +2382,32 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
(current_tpt > (100 * tbl->expected_tpt[low]))))
scale_action = 0;
+ if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >=
+ IWL_BT_COEX_TRAFFIC_LOAD_HIGH) &&
+ (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) {
+ if (lq_sta->last_bt_traffic >
+ BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
+ /*
+ * don't set scale_action, don't want to scale up if
+ * the rate scale doesn't otherwise think that is a
+ * good idea.
+ */
+ } else if (lq_sta->last_bt_traffic <=
+ BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
+ scale_action = -1;
+ }
+ }
+ lq_sta->last_bt_traffic =
+ BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD);
+
+ if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >=
+ IWL_BT_COEX_TRAFFIC_LOAD_HIGH) &&
+ (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) {
+ /* search for a new modulation */
+ rs_stay_in_table(lq_sta, true);
+ goto lq_update;
+ }
+
switch (scale_action) {
case -1:
/* Decrease starting rate, update uCode's rate table */
@@ -2782,6 +2888,13 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm,
lq_cmd->agg_time_limit =
cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
+
+ /*
+ * overwrite if needed, pass aggregation time limit
+ * to uCode in uSec - This is racy - but heh, at least it helps...
+ */
+ if (mvm && BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= 2)
+ lq_cmd->agg_time_limit = cpu_to_le16(1200);
}
static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
@@ -3080,3 +3193,29 @@ void iwl_mvm_rate_control_unregister(void)
{
ieee80211_rate_control_unregister(&rs_mvm_ops);
}
+
+/**
+ * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable
+ * Tx protection, according to this rquest and previous requests,
+ * and send the LQ command.
+ * @lq: The LQ command
+ * @mvmsta: The station
+ * @enable: Enable Tx protection?
+ */
+int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
+ struct iwl_mvm_sta *mvmsta, bool enable)
+{
+ lockdep_assert_held(&mvm->mutex);
+
+ if (enable) {
+ if (mvmsta->tx_protection == 0)
+ lq->flags |= LQ_FLAG_SET_STA_TLC_RTS_MSK;
+ mvmsta->tx_protection++;
+ } else {
+ mvmsta->tx_protection--;
+ if (mvmsta->tx_protection == 0)
+ lq->flags &= ~LQ_FLAG_SET_STA_TLC_RTS_MSK;
+ }
+
+ return iwl_mvm_send_lq_cmd(mvm, lq, CMD_ASYNC, false);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h
index 219c6857cc0f..cff4f6da7733 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.h
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.h
@@ -358,6 +358,18 @@ struct iwl_lq_sta {
u8 last_bt_traffic;
};
+enum iwl_bt_coex_profile_traffic_load {
+ IWL_BT_COEX_TRAFFIC_LOAD_NONE = 0,
+ IWL_BT_COEX_TRAFFIC_LOAD_LOW = 1,
+ IWL_BT_COEX_TRAFFIC_LOAD_HIGH = 2,
+ IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS = 3,
+/*
+ * There are no more even though below is a u8, the
+ * indication from the BT device only has two bits.
+ */
+};
+
+
static inline u8 num_of_ant(u8 mask)
{
return !!((mask) & ANT_A) +
@@ -390,4 +402,9 @@ extern int iwl_mvm_rate_control_register(void);
*/
extern void iwl_mvm_rate_control_unregister(void);
+struct iwl_mvm_sta;
+
+int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
+ struct iwl_mvm_sta *mvmsta, bool enable);
+
#endif /* __rs__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c
index 4dfc21a3e83e..e4930d5027d2 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rx.c
@@ -363,3 +363,25 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
rxb, &rx_status);
return 0;
}
+
+/*
+ * iwl_mvm_rx_statistics - STATISTICS_NOTIFICATION handler
+ *
+ * TODO: This handler is implemented partially.
+ * It only gets the NIC's temperature.
+ */
+int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_notif_statistics *stats = (void *)&pkt->data;
+ struct mvm_statistics_general_common *common = &stats->general.common;
+
+ if (mvm->temperature != le32_to_cpu(common->temperature)) {
+ mvm->temperature = le32_to_cpu(common->temperature);
+ iwl_mvm_tt_handler(mvm);
+ }
+
+ return 0;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c
index 2476e43799d5..2157b0f8ced5 100644
--- a/drivers/net/wireless/iwlwifi/mvm/scan.c
+++ b/drivers/net/wireless/iwlwifi/mvm/scan.c
@@ -298,12 +298,6 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
else
cmd->type = cpu_to_le32(SCAN_TYPE_FORCED);
- /*
- * TODO: This is a WA due to a bug in the FW AUX framework that does not
- * properly handle time events that fail to be scheduled
- */
- cmd->type = cpu_to_le32(SCAN_TYPE_FORCED);
-
cmd->repeats = cpu_to_le32(1);
/*
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
index 5c664ed54400..2278858d5658 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.c
@@ -64,6 +64,7 @@
#include "mvm.h"
#include "sta.h"
+#include "rs.h"
static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm)
{
@@ -217,6 +218,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
mvmvif->color);
mvm_sta->vif = vif;
mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
+ mvm_sta->tx_protection = 0;
+ mvm_sta->tt_tx_protection = false;
/* HW restart, don't assume the memory has been zeroed */
atomic_set(&mvm->pending_frames[sta_id], 0);
@@ -798,21 +801,23 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
min(mvmsta->max_agg_bufsize, buf_size);
mvmsta->lq_sta.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize;
+ IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",
+ sta->addr, tid);
+
if (mvm->cfg->ht_params->use_rts_for_aggregation) {
/*
* switch to RTS/CTS if it is the prefer protection
* method for HT traffic
+ * this function also sends the LQ command
*/
- mvmsta->lq_sta.lq.flags |= LQ_FLAG_SET_STA_TLC_RTS_MSK;
+ return iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq,
+ mvmsta, true);
/*
* TODO: remove the TLC_RTS flag when we tear down the last
* AGG session (agg_tids_count in DVM)
*/
}
- IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",
- sta->addr, tid);
-
return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, CMD_ASYNC, false);
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h
index a4ddce77aaae..3efa0a0cc987 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.h
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.h
@@ -275,6 +275,8 @@ struct iwl_mvm_tid_data {
* @lock: lock to protect the whole struct. Since %tid_data is access from Tx
* and from Tx response flow, it needs a spinlock.
* @tid_data: per tid data. Look at %iwl_mvm_tid_data.
+ * @tx_protection: reference counter for controlling the Tx protection.
+ * @tt_tx_protection: is thermal throttling enable Tx protection?
*
* When mac80211 creates a station it reserves some space (hw->sta_data_size)
* in the structure for use by driver. This structure is placed in that
@@ -296,6 +298,10 @@ struct iwl_mvm_sta {
#ifdef CONFIG_PM_SLEEP
u16 last_seq_ctl;
#endif
+
+ /* Temporary, until the new TLC will control the Tx protection */
+ s8 tx_protection;
+ bool tt_tx_protection;
};
/**
diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c
new file mode 100644
index 000000000000..a7e3b8ddf22b
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/tt.c
@@ -0,0 +1,512 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ *
+ * 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, 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,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * 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.
+ * * Neither the name Intel Corporation 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 BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 "mvm.h"
+#include "iwl-config.h"
+#include "iwl-io.h"
+#include "iwl-csr.h"
+#include "iwl-prph.h"
+
+#define OTP_DTS_DIODE_DEVIATION 96 /*in words*/
+/* VBG - Voltage Band Gap error data (temperature offset) */
+#define OTP_WP_DTS_VBG (OTP_DTS_DIODE_DEVIATION + 2)
+#define MEAS_VBG_MIN_VAL 2300
+#define MEAS_VBG_MAX_VAL 3000
+#define MEAS_VBG_DEFAULT_VAL 2700
+#define DTS_DIODE_VALID(flags) (flags & DTS_DIODE_REG_FLAGS_PASS_ONCE)
+#define MIN_TEMPERATURE 0
+#define MAX_TEMPERATURE 125
+#define TEMPERATURE_ERROR (MAX_TEMPERATURE + 1)
+#define PTAT_DIGITAL_VALUE_MIN_VALUE 0
+#define PTAT_DIGITAL_VALUE_MAX_VALUE 0xFF
+#define DTS_VREFS_NUM 5
+static inline u32 DTS_DIODE_GET_VREFS_ID(u32 flags)
+{
+ return (flags & DTS_DIODE_REG_FLAGS_VREFS_ID) >>
+ DTS_DIODE_REG_FLAGS_VREFS_ID_POS;
+}
+
+#define CALC_VREFS_MIN_DIFF 43
+#define CALC_VREFS_MAX_DIFF 51
+#define CALC_LUT_SIZE (1 + CALC_VREFS_MAX_DIFF - CALC_VREFS_MIN_DIFF)
+#define CALC_LUT_INDEX_OFFSET CALC_VREFS_MIN_DIFF
+#define CALC_TEMPERATURE_RESULT_SHIFT_OFFSET 23
+
+/*
+ * @digital_value: The diode's digital-value sampled (temperature/voltage)
+ * @vref_low: The lower voltage-reference (the vref just below the diode's
+ * sampled digital-value)
+ * @vref_high: The higher voltage-reference (the vref just above the diode's
+ * sampled digital-value)
+ * @flags: bits[1:0]: The ID of the Vrefs pair (lowVref,highVref)
+ * bits[6:2]: Reserved.
+ * bits[7:7]: Indicates completion of at least 1 successful sample
+ * since last DTS reset.
+ */
+struct iwl_mvm_dts_diode_bits {
+ u8 digital_value;
+ u8 vref_low;
+ u8 vref_high;
+ u8 flags;
+} __packed;
+
+union dts_diode_results {
+ u32 reg_value;
+ struct iwl_mvm_dts_diode_bits bits;
+} __packed;
+
+static s16 iwl_mvm_dts_get_volt_band_gap(struct iwl_mvm *mvm)
+{
+ struct iwl_nvm_section calib_sec;
+ const __le16 *calib;
+ u16 vbg;
+
+ /* TODO: move parsing to NVM code */
+ calib_sec = mvm->nvm_sections[NVM_SECTION_TYPE_CALIBRATION];
+ calib = (__le16 *)calib_sec.data;
+
+ vbg = le16_to_cpu(calib[OTP_WP_DTS_VBG]);
+
+ if (vbg < MEAS_VBG_MIN_VAL || vbg > MEAS_VBG_MAX_VAL)
+ vbg = MEAS_VBG_DEFAULT_VAL;
+
+ return vbg;
+}
+
+static u16 iwl_mvm_dts_get_ptat_deviation_offset(struct iwl_mvm *mvm)
+{
+ const u8 *calib;
+ u8 ptat, pa1, pa2, median;
+
+ /* TODO: move parsing to NVM code */
+ calib = mvm->nvm_sections[NVM_SECTION_TYPE_CALIBRATION].data;
+ ptat = calib[OTP_DTS_DIODE_DEVIATION];
+ pa1 = calib[OTP_DTS_DIODE_DEVIATION + 1];
+ pa2 = calib[OTP_DTS_DIODE_DEVIATION + 2];
+
+ /* get the median: */
+ if (ptat > pa1) {
+ if (ptat > pa2)
+ median = (pa1 > pa2) ? pa1 : pa2;
+ else
+ median = ptat;
+ } else {
+ if (pa1 > pa2)
+ median = (ptat > pa2) ? ptat : pa2;
+ else
+ median = pa1;
+ }
+
+ return ptat - median;
+}
+
+static u8 iwl_mvm_dts_calibrate_ptat_deviation(struct iwl_mvm *mvm, u8 value)
+{
+ /* Calibrate the PTAT digital value, based on PTAT deviation data: */
+ s16 new_val = value - iwl_mvm_dts_get_ptat_deviation_offset(mvm);
+
+ if (new_val > PTAT_DIGITAL_VALUE_MAX_VALUE)
+ new_val = PTAT_DIGITAL_VALUE_MAX_VALUE;
+ else if (new_val < PTAT_DIGITAL_VALUE_MIN_VALUE)
+ new_val = PTAT_DIGITAL_VALUE_MIN_VALUE;
+
+ return new_val;
+}
+
+static bool dts_get_adjacent_vrefs(struct iwl_mvm *mvm,
+ union dts_diode_results *avg_ptat)
+{
+ u8 vrefs_results[DTS_VREFS_NUM];
+ u8 low_vref_index = 0, flags;
+ u32 reg;
+
+ reg = iwl_read_prph(mvm->trans, DTSC_VREF_AVG);
+ memcpy(vrefs_results, &reg, sizeof(reg));
+ reg = iwl_read_prph(mvm->trans, DTSC_VREF5_AVG);
+ vrefs_results[4] = reg & 0xff;
+
+ if (avg_ptat->bits.digital_value < vrefs_results[0] ||
+ avg_ptat->bits.digital_value > vrefs_results[4])
+ return false;
+
+ if (avg_ptat->bits.digital_value > vrefs_results[3])
+ low_vref_index = 3;
+ else if (avg_ptat->bits.digital_value > vrefs_results[2])
+ low_vref_index = 2;
+ else if (avg_ptat->bits.digital_value > vrefs_results[1])
+ low_vref_index = 1;
+
+ avg_ptat->bits.vref_low = vrefs_results[low_vref_index];
+ avg_ptat->bits.vref_high = vrefs_results[low_vref_index + 1];
+ flags = avg_ptat->bits.flags;
+ avg_ptat->bits.flags =
+ (flags & ~DTS_DIODE_REG_FLAGS_VREFS_ID) |
+ (low_vref_index & DTS_DIODE_REG_FLAGS_VREFS_ID);
+ return true;
+}
+
+/*
+ * return true it the results are valid, and false otherwise.
+ */
+static bool dts_read_ptat_avg_results(struct iwl_mvm *mvm,
+ union dts_diode_results *avg_ptat)
+{
+ u32 reg;
+ u8 tmp;
+
+ /* fill the diode value and pass_once with avg-reg results */
+ reg = iwl_read_prph(mvm->trans, DTSC_PTAT_AVG);
+ reg &= DTS_DIODE_REG_DIG_VAL | DTS_DIODE_REG_PASS_ONCE;
+ avg_ptat->reg_value = reg;
+
+ /* calibrate the PTAT digital value */
+ tmp = avg_ptat->bits.digital_value;
+ tmp = iwl_mvm_dts_calibrate_ptat_deviation(mvm, tmp);
+ avg_ptat->bits.digital_value = tmp;
+
+ /*
+ * fill vrefs fields, based on the avgVrefs results
+ * and the diode value
+ */
+ return dts_get_adjacent_vrefs(mvm, avg_ptat) &&
+ DTS_DIODE_VALID(avg_ptat->bits.flags);
+}
+
+static s32 calculate_nic_temperature(union dts_diode_results avg_ptat,
+ u16 volt_band_gap)
+{
+ u32 tmp_result;
+ u8 vrefs_diff;
+ /*
+ * For temperature calculation (at the end, shift right by 23)
+ * LUT[(D2-D1)] = ROUND{ 2^23 / ((D2-D1)*9*10) }
+ * (D2-D1) == 43 44 45 46 47 48 49 50 51
+ */
+ static const u16 calc_lut[CALC_LUT_SIZE] = {
+ 2168, 2118, 2071, 2026, 1983, 1942, 1902, 1864, 1828,
+ };
+
+ /*
+ * The diff between the high and low voltage-references is assumed
+ * to be strictly be in range of [60,68]
+ */
+ vrefs_diff = avg_ptat.bits.vref_high - avg_ptat.bits.vref_low;
+
+ if (vrefs_diff < CALC_VREFS_MIN_DIFF ||
+ vrefs_diff > CALC_VREFS_MAX_DIFF)
+ return TEMPERATURE_ERROR;
+
+ /* calculate the result: */
+ tmp_result =
+ vrefs_diff * (DTS_DIODE_GET_VREFS_ID(avg_ptat.bits.flags) + 9);
+ tmp_result += avg_ptat.bits.digital_value;
+ tmp_result -= avg_ptat.bits.vref_high;
+
+ /* multiply by the LUT value (based on the diff) */
+ tmp_result *= calc_lut[vrefs_diff - CALC_LUT_INDEX_OFFSET];
+
+ /*
+ * Get the BandGap (the voltage refereces source) error data
+ * (temperature offset)
+ */
+ tmp_result *= volt_band_gap;
+
+ /*
+ * here, tmp_result value can be up to 32-bits. We want to right-shift
+ * it *without* sign-extend.
+ */
+ tmp_result = tmp_result >> CALC_TEMPERATURE_RESULT_SHIFT_OFFSET;
+
+ /*
+ * at this point, tmp_result should be in the range:
+ * 200 <= tmp_result <= 365
+ */
+ return (s16)tmp_result - 240;
+}
+
+static s32 check_nic_temperature(struct iwl_mvm *mvm)
+{
+ u16 volt_band_gap;
+ union dts_diode_results avg_ptat;
+
+ volt_band_gap = iwl_mvm_dts_get_volt_band_gap(mvm);
+
+ /* disable DTS */
+ iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 0);
+
+ /* SV initialization */
+ iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 1);
+ iwl_write_prph(mvm->trans, DTSC_CFG_MODE,
+ DTSC_CFG_MODE_PERIODIC);
+
+ /* wait for results */
+ msleep(100);
+ if (!dts_read_ptat_avg_results(mvm, &avg_ptat))
+ return TEMPERATURE_ERROR;
+
+ /* disable DTS */
+ iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 0);
+
+ return calculate_nic_temperature(avg_ptat, volt_band_gap);
+}
+
+static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm)
+{
+ u32 duration = mvm->thermal_throttle.params->ct_kill_duration;
+
+ IWL_ERR(mvm, "Enter CT Kill\n");
+ iwl_mvm_set_hw_ctkill_state(mvm, true);
+ schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
+ round_jiffies_relative(duration * HZ));
+}
+
+static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm)
+{
+ IWL_ERR(mvm, "Exit CT Kill\n");
+ iwl_mvm_set_hw_ctkill_state(mvm, false);
+}
+
+static void check_exit_ctkill(struct work_struct *work)
+{
+ struct iwl_mvm_tt_mgmt *tt;
+ struct iwl_mvm *mvm;
+ u32 duration;
+ s32 temp;
+
+ tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work);
+ mvm = container_of(tt, struct iwl_mvm, thermal_throttle);
+
+ duration = tt->params->ct_kill_duration;
+
+ iwl_trans_start_hw(mvm->trans);
+ temp = check_nic_temperature(mvm);
+ iwl_trans_stop_hw(mvm->trans, false);
+
+ if (temp < MIN_TEMPERATURE || temp > MAX_TEMPERATURE) {
+ IWL_DEBUG_TEMP(mvm, "Failed to measure NIC temperature\n");
+ goto reschedule;
+ }
+ IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp);
+
+ if (temp <= tt->params->ct_kill_exit) {
+ iwl_mvm_exit_ctkill(mvm);
+ return;
+ }
+
+reschedule:
+ schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
+ round_jiffies(duration * HZ));
+}
+
+static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm *mvm = _data;
+ enum ieee80211_smps_mode smps_mode;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (mvm->thermal_throttle.dynamic_smps)
+ smps_mode = IEEE80211_SMPS_DYNAMIC;
+ else
+ smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode);
+}
+
+static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
+{
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvmsta;
+ int i, err;
+
+ for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+ lockdep_is_held(&mvm->mutex));
+ if (IS_ERR_OR_NULL(sta))
+ continue;
+ mvmsta = (void *)sta->drv_priv;
+ if (enable == mvmsta->tt_tx_protection)
+ continue;
+ err = iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq,
+ mvmsta, enable);
+ if (err) {
+ IWL_ERR(mvm, "Failed to %s Tx protection\n",
+ enable ? "enable" : "disable");
+ } else {
+ IWL_DEBUG_TEMP(mvm, "%s Tx protection\n",
+ enable ? "Enable" : "Disable");
+ mvmsta->tt_tx_protection = enable;
+ }
+ }
+}
+
+static void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
+{
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_THERMAL_MNG_BACKOFF,
+ .len = { sizeof(u32), },
+ .data = { &backoff, },
+ .flags = CMD_SYNC,
+ };
+
+ if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {
+ IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",
+ backoff);
+ mvm->thermal_throttle.tx_backoff = backoff;
+ } else {
+ IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n");
+ }
+}
+
+void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
+{
+ const struct iwl_tt_params *params = mvm->thermal_throttle.params;
+ struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
+ s32 temperature = mvm->temperature;
+ int i;
+ u32 tx_backoff;
+
+ IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature);
+
+ if (params->support_ct_kill && temperature >= params->ct_kill_entry) {
+ iwl_mvm_enter_ctkill(mvm);
+ return;
+ }
+
+ if (params->support_dynamic_smps) {
+ if (!tt->dynamic_smps &&
+ temperature >= params->dynamic_smps_entry) {
+ IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n");
+ tt->dynamic_smps = true;
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_tt_smps_iterator, mvm);
+ } else if (tt->dynamic_smps &&
+ temperature <= params->dynamic_smps_exit) {
+ IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n");
+ tt->dynamic_smps = false;
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_tt_smps_iterator, mvm);
+ }
+ }
+
+ if (params->support_tx_protection) {
+ if (temperature >= params->tx_protection_entry)
+ iwl_mvm_tt_tx_protection(mvm, true);
+ else if (temperature <= params->tx_protection_exit)
+ iwl_mvm_tt_tx_protection(mvm, false);
+ }
+
+ if (params->support_tx_backoff) {
+ tx_backoff = 0;
+ for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) {
+ if (temperature < params->tx_backoff[i].temperature)
+ break;
+ tx_backoff = params->tx_backoff[i].backoff;
+ }
+ if (tt->tx_backoff != tx_backoff)
+ iwl_mvm_tt_tx_backoff(mvm, tx_backoff);
+ }
+}
+
+static const struct iwl_tt_params iwl7000_tt_params = {
+ .ct_kill_entry = 118,
+ .ct_kill_exit = 96,
+ .ct_kill_duration = 5,
+ .dynamic_smps_entry = 114,
+ .dynamic_smps_exit = 110,
+ .tx_protection_entry = 114,
+ .tx_protection_exit = 108,
+ .tx_backoff = {
+ {.temperature = 112, .backoff = 200},
+ {.temperature = 113, .backoff = 600},
+ {.temperature = 114, .backoff = 1200},
+ {.temperature = 115, .backoff = 2000},
+ {.temperature = 116, .backoff = 4000},
+ {.temperature = 117, .backoff = 10000},
+ },
+ .support_ct_kill = true,
+ .support_dynamic_smps = true,
+ .support_tx_protection = true,
+ .support_tx_backoff = true,
+};
+
+void iwl_mvm_tt_initialize(struct iwl_mvm *mvm)
+{
+ struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
+
+ IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
+ tt->params = &iwl7000_tt_params;
+ INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
+}
+
+void iwl_mvm_tt_exit(struct iwl_mvm *mvm)
+{
+ cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
+ IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c
index f212f16502ff..a830eb6cc411 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tx.c
@@ -175,7 +175,7 @@ static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm,
* table is controlled by LINK_QUALITY commands
*/
- if (ieee80211_is_data(fc)) {
+ if (ieee80211_is_data(fc) && sta) {
tx_cmd->initial_rate_index = 0;
tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE);
return;
diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c
index 687b34e387ac..1e1332839e4a 100644
--- a/drivers/net/wireless/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/iwlwifi/mvm/utils.c
@@ -76,6 +76,11 @@ int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd)
{
int ret;
+#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP)
+ if (WARN_ON(mvm->d3_test_active))
+ return -EIO;
+#endif
+
/*
* Synchronous commands from this op-mode must hold
* the mutex, this ensures we don't try to send two
@@ -125,6 +130,11 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
lockdep_assert_held(&mvm->mutex);
+#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP)
+ if (WARN_ON(mvm->d3_test_active))
+ return -EIO;
+#endif
+
/*
* Only synchronous commands can wait for status,
* we use WANT_SKB so the caller can't.
@@ -471,3 +481,34 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
return iwl_mvm_send_cmd(mvm, &cmd);
}
+
+/**
+ * iwl_mvm_update_smps - Get a requst to change the SMPS mode
+ * @req_type: The part of the driver who call for a change.
+ * @smps_requests: The request to change the SMPS mode.
+ *
+ * Get a requst to change the SMPS mode,
+ * and change it according to all other requests in the driver.
+ */
+void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ enum iwl_mvm_smps_type_request req_type,
+ enum ieee80211_smps_mode smps_request)
+{
+ struct iwl_mvm_vif *mvmvif;
+ enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC;
+ int i;
+
+ lockdep_assert_held(&mvm->mutex);
+ mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ mvmvif->smps_requests[req_type] = smps_request;
+ for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) {
+ if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC) {
+ smps_mode = IEEE80211_SMPS_STATIC;
+ break;
+ }
+ if (mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC)
+ smps_mode = IEEE80211_SMPS_DYNAMIC;
+ }
+
+ ieee80211_request_smps(vif, smps_mode);
+}
diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c
index 8cb53ec2b77b..db7bdd35a9c5 100644
--- a/drivers/net/wireless/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/iwlwifi/pcie/drv.c
@@ -256,10 +256,54 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
/* 7000 Series */
{IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4160, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4162, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4270, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4260, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4262, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4470, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4460, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)},
{IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_ac_cfg)},
- {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC170, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC060, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC160, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC062, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC162, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC270, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC260, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC262, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC470, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC460, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC462, iwl7260_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC020, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B2, 0xC220, iwl7260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0xC420, iwl7260_2n_cfg)},
+
+/* 3160 Series */
+ {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0170, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0060, iwl3160_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0062, iwl3160_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x0270, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x0470, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8170, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8060, iwl3160_2n_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)},
+ {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)},
{0}
};
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 50ba0a468f94..197dbe0a868c 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -405,20 +405,27 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num,
{
u8 *v_addr;
dma_addr_t p_addr;
- u32 offset;
+ u32 offset, chunk_sz = section->len;
int ret = 0;
IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n",
section_num);
- v_addr = dma_alloc_coherent(trans->dev, PAGE_SIZE, &p_addr, GFP_KERNEL);
- if (!v_addr)
- return -ENOMEM;
+ v_addr = dma_alloc_coherent(trans->dev, chunk_sz, &p_addr,
+ GFP_KERNEL | __GFP_NOWARN);
+ if (!v_addr) {
+ IWL_DEBUG_INFO(trans, "Falling back to small chunks of DMA\n");
+ chunk_sz = PAGE_SIZE;
+ v_addr = dma_alloc_coherent(trans->dev, chunk_sz,
+ &p_addr, GFP_KERNEL);
+ if (!v_addr)
+ return -ENOMEM;
+ }
- for (offset = 0; offset < section->len; offset += PAGE_SIZE) {
+ for (offset = 0; offset < section->len; offset += chunk_sz) {
u32 copy_size;
- copy_size = min_t(u32, PAGE_SIZE, section->len - offset);
+ copy_size = min_t(u32, chunk_sz, section->len - offset);
memcpy(v_addr, (u8 *)section->data + offset, copy_size);
ret = iwl_pcie_load_firmware_chunk(trans,
@@ -432,7 +439,7 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num,
}
}
- dma_free_coherent(trans->dev, PAGE_SIZE, v_addr, p_addr);
+ dma_free_coherent(trans->dev, chunk_sz, v_addr, p_addr);
return ret;
}
@@ -571,13 +578,17 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
clear_bit(STATUS_RFKILL, &trans_pcie->status);
}
-static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans)
+static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test)
{
- /* let the ucode operate on its own */
- iwl_write32(trans, CSR_UCODE_DRV_GP1_SET,
- CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
-
iwl_disable_interrupts(trans);
+
+ /*
+ * in testing mode, the host stays awake and the
+ * hardware won't be reset (not even partially)
+ */
+ if (test)
+ return;
+
iwl_pcie_disable_ict(trans);
iwl_clear_bit(trans, CSR_GP_CNTRL,
@@ -596,11 +607,18 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans)
}
static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
- enum iwl_d3_status *status)
+ enum iwl_d3_status *status,
+ bool test)
{
u32 val;
int ret;
+ if (test) {
+ iwl_enable_interrupts(trans);
+ *status = IWL_D3_STATUS_ALIVE;
+ return 0;
+ }
+
iwl_pcie_set_pwr(trans, false);
val = iwl_read32(trans, CSR_RESET);
@@ -636,9 +654,6 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
return ret;
}
- iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR,
- CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
-
*status = IWL_D3_STATUS_ALIVE;
return 0;
}
@@ -917,11 +932,11 @@ static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr,
}
static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr,
- void *buf, int dwords)
+ const void *buf, int dwords)
{
unsigned long flags;
int offs, ret = 0;
- u32 *vals = buf;
+ const u32 *vals = buf;
if (iwl_trans_grab_nic_access(trans, false, &flags)) {
iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr);
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index c5e30294c5ac..f65da1984d91 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -224,13 +224,13 @@ static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans,
switch (sec_ctl & TX_CMD_SEC_MSK) {
case TX_CMD_SEC_CCM:
- len += CCMP_MIC_LEN;
+ len += IEEE80211_CCMP_MIC_LEN;
break;
case TX_CMD_SEC_TKIP:
- len += TKIP_ICV_LEN;
+ len += IEEE80211_TKIP_ICV_LEN;
break;
case TX_CMD_SEC_WEP:
- len += WEP_IV_LEN + WEP_ICV_LEN;
+ len += IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN;
break;
}
@@ -1045,6 +1045,10 @@ static inline void iwl_pcie_txq_set_inactive(struct iwl_trans *trans,
(1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
}
+/* Receiver address (actually, Rx station's index into station table),
+ * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */
+#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid))
+
void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
int sta_id, int tid, int frame_limit, u16 ssn)
{
@@ -1518,11 +1522,13 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) {
IWL_ERR(trans, "FW error in SYNC CMD %s\n",
get_cmd_string(trans_pcie, cmd->id));
+ dump_stack();
ret = -EIO;
goto cancel;
}
- if (test_bit(STATUS_RFKILL, &trans_pcie->status)) {
+ if (!(cmd->flags & CMD_SEND_IN_RFKILL) &&
+ test_bit(STATUS_RFKILL, &trans_pcie->status)) {
IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n");
ret = -ERFKILL;
goto cancel;
@@ -1564,7 +1570,8 @@ int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
if (test_bit(STATUS_FW_ERROR, &trans_pcie->status))
return -EIO;
- if (test_bit(STATUS_RFKILL, &trans_pcie->status)) {
+ if (!(cmd->flags & CMD_SEND_IN_RFKILL) &&
+ test_bit(STATUS_RFKILL, &trans_pcie->status)) {
IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n",
cmd->id);
return -ERFKILL;
diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig
index 4f614aad9ded..f7ff4725506a 100644
--- a/drivers/net/wireless/mwifiex/Kconfig
+++ b/drivers/net/wireless/mwifiex/Kconfig
@@ -3,13 +3,13 @@ config MWIFIEX
depends on CFG80211
---help---
This adds support for wireless adapters based on Marvell
- 802.11n chipsets.
+ 802.11n/ac chipsets.
If you choose to build it as a module, it will be called
mwifiex.
config MWIFIEX_SDIO
- tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797"
+ tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8897"
depends on MWIFIEX && MMC
select FW_LOADER
---help---
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index e42b266a023a..00a82817eb6b 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -1231,6 +1231,51 @@ static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy,
return 0;
}
+/* cfg80211 operation handler for del_station.
+ * Function deauthenticates station which value is provided in mac parameter.
+ * If mac is NULL/broadcast, all stations in associated station list are
+ * deauthenticated. If bss is not started or there are no stations in
+ * associated stations list, no action is taken.
+ */
+static int
+mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
+ u8 *mac)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ struct mwifiex_sta_node *sta_node;
+ unsigned long flags;
+
+ if (list_empty(&priv->sta_list) || !priv->bss_started)
+ return 0;
+
+ if (!mac || is_broadcast_ether_addr(mac)) {
+ wiphy_dbg(wiphy, "%s: NULL/broadcast mac address\n", __func__);
+ list_for_each_entry(sta_node, &priv->sta_list, list) {
+ if (mwifiex_send_cmd_sync(priv,
+ HostCmd_CMD_UAP_STA_DEAUTH,
+ HostCmd_ACT_GEN_SET, 0,
+ sta_node->mac_addr))
+ return -1;
+ mwifiex_uap_del_sta_data(priv, sta_node);
+ }
+ } else {
+ wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__, mac);
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ sta_node = mwifiex_get_sta_entry(priv, mac);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+ if (sta_node) {
+ if (mwifiex_send_cmd_sync(priv,
+ HostCmd_CMD_UAP_STA_DEAUTH,
+ HostCmd_ACT_GEN_SET, 0,
+ sta_node->mac_addr))
+ return -1;
+ mwifiex_uap_del_sta_data(priv, sta_node);
+ }
+ }
+
+ return 0;
+}
+
static int
mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
{
@@ -1859,6 +1904,7 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
int i, offset, ret;
struct ieee80211_channel *chan;
struct ieee_types_header *ie;
+ struct mwifiex_user_scan_cfg *user_scan_cfg;
wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name);
@@ -1869,20 +1915,22 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
return -EBUSY;
}
- if (priv->user_scan_cfg) {
+ /* Block scan request if scan operation or scan cleanup when interface
+ * is disabled is in process
+ */
+ if (priv->scan_request || priv->scan_aborting) {
dev_err(priv->adapter->dev, "cmd: Scan already in process..\n");
return -EBUSY;
}
- priv->user_scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg),
- GFP_KERNEL);
- if (!priv->user_scan_cfg)
+ user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL);
+ if (!user_scan_cfg)
return -ENOMEM;
priv->scan_request = request;
- priv->user_scan_cfg->num_ssids = request->n_ssids;
- priv->user_scan_cfg->ssid_list = request->ssids;
+ user_scan_cfg->num_ssids = request->n_ssids;
+ user_scan_cfg->ssid_list = request->ssids;
if (request->ie && request->ie_len) {
offset = 0;
@@ -1902,25 +1950,25 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
for (i = 0; i < min_t(u32, request->n_channels,
MWIFIEX_USER_SCAN_CHAN_MAX); i++) {
chan = request->channels[i];
- priv->user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
- priv->user_scan_cfg->chan_list[i].radio_type = chan->band;
+ user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
+ user_scan_cfg->chan_list[i].radio_type = chan->band;
if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
- priv->user_scan_cfg->chan_list[i].scan_type =
+ user_scan_cfg->chan_list[i].scan_type =
MWIFIEX_SCAN_TYPE_PASSIVE;
else
- priv->user_scan_cfg->chan_list[i].scan_type =
+ user_scan_cfg->chan_list[i].scan_type =
MWIFIEX_SCAN_TYPE_ACTIVE;
- priv->user_scan_cfg->chan_list[i].scan_time = 0;
+ user_scan_cfg->chan_list[i].scan_time = 0;
}
- ret = mwifiex_scan_networks(priv, priv->user_scan_cfg);
+ ret = mwifiex_scan_networks(priv, user_scan_cfg);
+ kfree(user_scan_cfg);
if (ret) {
dev_err(priv->adapter->dev, "scan failed: %d\n", ret);
+ priv->scan_aborting = false;
priv->scan_request = NULL;
- kfree(priv->user_scan_cfg);
- priv->user_scan_cfg = NULL;
return ret;
}
@@ -2419,6 +2467,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
.change_beacon = mwifiex_cfg80211_change_beacon,
.set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config,
.set_antenna = mwifiex_cfg80211_set_antenna,
+ .del_station = mwifiex_cfg80211_del_station,
#ifdef CONFIG_PM
.suspend = mwifiex_cfg80211_suspend,
.resume = mwifiex_cfg80211_resume,
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c
index 26755d9acb55..2d761477d15e 100644
--- a/drivers/net/wireless/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/mwifiex/cmdevt.c
@@ -570,6 +570,7 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no,
case HostCmd_CMD_UAP_SYS_CONFIG:
case HostCmd_CMD_UAP_BSS_START:
case HostCmd_CMD_UAP_BSS_STOP:
+ case HostCmd_CMD_UAP_STA_DEAUTH:
ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action,
cmd_oid, data_buf,
cmd_ptr);
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h
index 1f7578d553ec..d6ada7354c14 100644
--- a/drivers/net/wireless/mwifiex/fw.h
+++ b/drivers/net/wireless/mwifiex/fw.h
@@ -271,6 +271,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075
#define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f
#define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083
+#define HostCmd_CMD_CFG_DATA 0x008f
#define HostCmd_CMD_VERSION_EXT 0x0097
#define HostCmd_CMD_MEF_CFG 0x009a
#define HostCmd_CMD_RSSI_INFO 0x00a4
@@ -279,6 +280,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define HostCmd_CMD_UAP_SYS_CONFIG 0x00b0
#define HostCmd_CMD_UAP_BSS_START 0x00b1
#define HostCmd_CMD_UAP_BSS_STOP 0x00b2
+#define HostCmd_CMD_UAP_STA_DEAUTH 0x00b5
#define HostCmd_CMD_11N_CFG 0x00cd
#define HostCmd_CMD_11N_ADDBA_REQ 0x00ce
#define HostCmd_CMD_11N_ADDBA_RSP 0x00cf
@@ -464,6 +466,8 @@ enum P2P_MODES {
#define MWIFIEX_CRITERIA_UNICAST BIT(1)
#define MWIFIEX_CRITERIA_MULTICAST BIT(3)
+#define CFG_DATA_TYPE_CAL 2
+
struct mwifiex_ie_types_header {
__le16 type;
__le16 len;
@@ -1197,6 +1201,11 @@ struct host_cmd_ds_amsdu_aggr_ctrl {
__le16 curr_buf_size;
} __packed;
+struct host_cmd_ds_sta_deauth {
+ u8 mac[ETH_ALEN];
+ __le16 reason;
+} __packed;
+
struct mwifiex_ie_types_wmm_param_set {
struct mwifiex_ie_types_header header;
u8 wmm_ie[1];
@@ -1573,6 +1582,12 @@ struct mwifiex_ie_list {
struct mwifiex_ie ie_list[MAX_MGMT_IE_INDEX];
} __packed;
+struct host_cmd_ds_802_11_cfg_data {
+ __le16 action;
+ __le16 type;
+ __le16 data_len;
+} __packed;
+
struct host_cmd_ds_command {
__le16 command;
__le16 size;
@@ -1630,7 +1645,9 @@ struct host_cmd_ds_command {
struct host_cmd_ds_802_11_eeprom_access eeprom;
struct host_cmd_ds_802_11_subsc_evt subsc_evt;
struct host_cmd_ds_sys_config uap_sys_config;
+ struct host_cmd_ds_sta_deauth sta_deauth;
struct host_cmd_11ac_vht_cfg vht_cfg;
+ struct host_cmd_ds_802_11_cfg_data cfg_data;
} params;
} __packed;
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c
index 9f44fda19db9..c7f11c0c3bb7 100644
--- a/drivers/net/wireless/mwifiex/init.c
+++ b/drivers/net/wireless/mwifiex/init.c
@@ -59,6 +59,9 @@ static void scan_delay_timer_fn(unsigned long data)
struct cmd_ctrl_node *cmd_node, *tmp_node;
unsigned long flags;
+ if (adapter->surprise_removed)
+ return;
+
if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) {
/*
* Abort scan operation by cancelling all pending scan
@@ -78,19 +81,13 @@ static void scan_delay_timer_fn(unsigned long data)
adapter->empty_tx_q_cnt = 0;
spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
- if (priv->user_scan_cfg) {
- if (priv->scan_request) {
- dev_dbg(priv->adapter->dev,
- "info: aborting scan\n");
- cfg80211_scan_done(priv->scan_request, 1);
- priv->scan_request = NULL;
- } else {
- dev_dbg(priv->adapter->dev,
- "info: scan already aborted\n");
- }
-
- kfree(priv->user_scan_cfg);
- priv->user_scan_cfg = NULL;
+ if (priv->scan_request) {
+ dev_dbg(adapter->dev, "info: aborting scan\n");
+ cfg80211_scan_done(priv->scan_request, 1);
+ priv->scan_request = NULL;
+ } else {
+ priv->scan_aborting = false;
+ dev_dbg(adapter->dev, "info: scan already aborted\n");
}
goto done;
}
@@ -447,23 +444,29 @@ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter)
}
/*
- * This function frees the adapter structure.
+ * This function performs cleanup for adapter structure.
*
- * The freeing operation is done recursively, by canceling all
- * pending commands, freeing the member buffers previously
- * allocated (command buffers, scan table buffer, sleep confirm
- * command buffer), stopping the timers and calling the cleanup
- * routines for every interface, before the actual adapter
- * structure is freed.
+ * The cleanup is done recursively, by canceling all pending
+ * commands, freeing the member buffers previously allocated
+ * (command buffers, scan table buffer, sleep confirm command
+ * buffer), stopping the timers and calling the cleanup routines
+ * for every interface.
*/
static void
-mwifiex_free_adapter(struct mwifiex_adapter *adapter)
+mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
{
+ int i;
+
if (!adapter) {
pr_err("%s: adapter is NULL\n", __func__);
return;
}
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i])
+ del_timer_sync(&adapter->priv[i]->scan_delay_timer);
+ }
+
mwifiex_cancel_all_pending_cmd(adapter);
/* Free lock variables */
@@ -684,7 +687,6 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
int ret = -EINPROGRESS;
struct mwifiex_private *priv;
s32 i;
- unsigned long flags;
struct sk_buff *skb;
/* mwifiex already shutdown */
@@ -719,7 +721,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
}
}
- spin_lock_irqsave(&adapter->mwifiex_lock, flags);
+ spin_lock(&adapter->mwifiex_lock);
if (adapter->if_ops.data_complete) {
while ((skb = skb_dequeue(&adapter->usb_rx_data_q))) {
@@ -733,10 +735,9 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
}
}
- /* Free adapter structure */
- mwifiex_free_adapter(adapter);
+ mwifiex_adapter_cleanup(adapter);
- spin_unlock_irqrestore(&adapter->mwifiex_lock, flags);
+ spin_unlock(&adapter->mwifiex_lock);
/* Notify completion */
ret = mwifiex_shutdown_fw_complete(adapter);
diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c
index 6bcb66e6e97c..122175af18c6 100644
--- a/drivers/net/wireless/mwifiex/join.c
+++ b/drivers/net/wireless/mwifiex/join.c
@@ -919,9 +919,8 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
memcpy(&priv->curr_bss_params.data_rates,
&adhoc_start->data_rate, priv->curr_bss_params.num_of_rates);
- dev_dbg(adapter->dev, "info: ADHOC_S_CMD: rates=%02x %02x %02x %02x\n",
- adhoc_start->data_rate[0], adhoc_start->data_rate[1],
- adhoc_start->data_rate[2], adhoc_start->data_rate[3]);
+ dev_dbg(adapter->dev, "info: ADHOC_S_CMD: rates=%4ph\n",
+ adhoc_start->data_rate);
dev_dbg(adapter->dev, "info: ADHOC_S_CMD: AD-HOC Start command is ready\n");
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c
index 2eb88ea9acf7..5bc7ef8d04d6 100644
--- a/drivers/net/wireless/mwifiex/main.c
+++ b/drivers/net/wireless/mwifiex/main.c
@@ -25,6 +25,8 @@
#define VERSION "1.0"
const char driver_version[] = "mwifiex " VERSION " (%s) ";
+static char *cal_data_cfg;
+module_param(cal_data_cfg, charp, 0);
/*
* This function registers the device and performs all the necessary
@@ -336,6 +338,13 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
dev_notice(adapter->dev, "WLAN FW is active\n");
+ if (cal_data_cfg) {
+ if ((request_firmware(&adapter->cal_data, cal_data_cfg,
+ adapter->dev)) < 0)
+ dev_err(adapter->dev,
+ "Cal data request_firmware() failed\n");
+ }
+
adapter->init_wait_q_woken = false;
ret = mwifiex_init_fw(adapter);
if (ret == -1) {
@@ -390,6 +399,10 @@ err_init_fw:
pr_debug("info: %s: unregister device\n", __func__);
adapter->if_ops.unregister_dev(adapter);
done:
+ if (adapter->cal_data) {
+ release_firmware(adapter->cal_data);
+ adapter->cal_data = NULL;
+ }
release_firmware(adapter->firmware);
complete(&adapter->fw_load);
return;
@@ -436,6 +449,7 @@ mwifiex_close(struct net_device *dev)
dev_dbg(priv->adapter->dev, "aborting scan on ndo_stop\n");
cfg80211_scan_done(priv->scan_request, 1);
priv->scan_request = NULL;
+ priv->scan_aborting = true;
}
return 0;
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index 4ef67fca06d3..0832c2437daf 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -492,7 +492,6 @@ struct mwifiex_private {
struct semaphore async_sem;
u8 report_scan_result;
struct cfg80211_scan_request *scan_request;
- struct mwifiex_user_scan_cfg *user_scan_cfg;
u8 cfg_bssid[6];
struct wps wps;
u8 scan_block;
@@ -510,6 +509,7 @@ struct mwifiex_private {
u8 ap_11ac_enabled;
u32 mgmt_frame_mask;
struct mwifiex_roc_cfg roc_cfg;
+ bool scan_aborting;
};
enum mwifiex_ba_status {
@@ -730,6 +730,7 @@ struct mwifiex_adapter {
u16 max_mgmt_ie_index;
u8 scan_delay_cnt;
u8 empty_tx_q_cnt;
+ const struct firmware *cal_data;
/* 11AC */
u32 is_hw_11ac_capable;
@@ -1115,6 +1116,8 @@ int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
struct cfg80211_beacon_data *data);
int mwifiex_del_mgmt_ies(struct mwifiex_private *priv);
u8 *mwifiex_11d_code_2_region(u8 code);
+void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
+ struct mwifiex_sta_node *node);
extern const struct ethtool_ops mwifiex_ethtool_ops;
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c
index 9cf5d8f07df8..801b6b728379 100644
--- a/drivers/net/wireless/mwifiex/scan.c
+++ b/drivers/net/wireless/mwifiex/scan.c
@@ -1784,22 +1784,17 @@ check_next_scan:
if (priv->report_scan_result)
priv->report_scan_result = false;
- if (priv->user_scan_cfg) {
- if (priv->scan_request) {
- dev_dbg(priv->adapter->dev,
- "info: notifying scan done\n");
- cfg80211_scan_done(priv->scan_request, 0);
- priv->scan_request = NULL;
- } else {
- dev_dbg(priv->adapter->dev,
- "info: scan already aborted\n");
- }
-
- kfree(priv->user_scan_cfg);
- priv->user_scan_cfg = NULL;
+ if (priv->scan_request) {
+ dev_dbg(adapter->dev, "info: notifying scan done\n");
+ cfg80211_scan_done(priv->scan_request, 0);
+ priv->scan_request = NULL;
+ } else {
+ priv->scan_aborting = false;
+ dev_dbg(adapter->dev, "info: scan already aborted\n");
}
} else {
- if (priv->user_scan_cfg && !priv->scan_request) {
+ if ((priv->scan_aborting && !priv->scan_request) ||
+ priv->scan_block) {
spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
flags);
adapter->scan_delay_cnt = MWIFIEX_MAX_SCAN_DELAY_CNT;
diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c
index 363ba31b58bf..5ee5ed02eccd 100644
--- a/drivers/net/wireless/mwifiex/sdio.c
+++ b/drivers/net/wireless/mwifiex/sdio.c
@@ -77,6 +77,17 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
+ if (id->driver_data) {
+ struct mwifiex_sdio_device *data = (void *)id->driver_data;
+
+ card->firmware = data->firmware;
+ card->reg = data->reg;
+ card->max_ports = data->max_ports;
+ card->mp_agg_pkt_limit = data->mp_agg_pkt_limit;
+ card->supports_sdio_new_mode = data->supports_sdio_new_mode;
+ card->has_control_mask = data->has_control_mask;
+ }
+
sdio_claim_host(func);
ret = sdio_enable_func(func);
sdio_release_host(func);
@@ -251,12 +262,19 @@ static int mwifiex_sdio_resume(struct device *dev)
#define SDIO_DEVICE_ID_MARVELL_8787 (0x9119)
/* Device ID for SD8797 */
#define SDIO_DEVICE_ID_MARVELL_8797 (0x9129)
+/* Device ID for SD8897 */
+#define SDIO_DEVICE_ID_MARVELL_8897 (0x912d)
/* WLAN IDs */
static const struct sdio_device_id mwifiex_ids[] = {
- {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786)},
- {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787)},
- {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797)},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786),
+ .driver_data = (unsigned long) &mwifiex_sdio_sd8786},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787),
+ .driver_data = (unsigned long) &mwifiex_sdio_sd8787},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797),
+ .driver_data = (unsigned long) &mwifiex_sdio_sd8797},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897),
+ .driver_data = (unsigned long) &mwifiex_sdio_sd8897},
{},
};
@@ -282,13 +300,13 @@ static struct sdio_driver mwifiex_sdio = {
* This function writes data into SDIO card register.
*/
static int
-mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u32 data)
+mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u8 data)
{
struct sdio_mmc_card *card = adapter->card;
int ret = -1;
sdio_claim_host(card->func);
- sdio_writeb(card->func, (u8) data, reg, &ret);
+ sdio_writeb(card->func, data, reg, &ret);
sdio_release_host(card->func);
return ret;
@@ -298,7 +316,7 @@ mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u32 data)
* This function reads data from SDIO card register.
*/
static int
-mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u32 *data)
+mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u8 *data)
{
struct sdio_mmc_card *card = adapter->card;
int ret = -1;
@@ -400,7 +418,40 @@ static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
}
/*
- * This function initializes the IO ports.
+ * This function is used to initialize IO ports for the
+ * chipsets supporting SDIO new mode eg SD8897.
+ */
+static int mwifiex_init_sdio_new_mode(struct mwifiex_adapter *adapter)
+{
+ u8 reg;
+
+ adapter->ioport = MEM_PORT;
+
+ /* enable sdio new mode */
+ if (mwifiex_read_reg(adapter, CARD_CONFIG_2_1_REG, &reg))
+ return -1;
+ if (mwifiex_write_reg(adapter, CARD_CONFIG_2_1_REG,
+ reg | CMD53_NEW_MODE))
+ return -1;
+
+ /* Configure cmd port and enable reading rx length from the register */
+ if (mwifiex_read_reg(adapter, CMD_CONFIG_0, &reg))
+ return -1;
+ if (mwifiex_write_reg(adapter, CMD_CONFIG_0, reg | CMD_PORT_RD_LEN_EN))
+ return -1;
+
+ /* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is
+ * completed
+ */
+ if (mwifiex_read_reg(adapter, CMD_CONFIG_1, &reg))
+ return -1;
+ if (mwifiex_write_reg(adapter, CMD_CONFIG_1, reg | CMD_PORT_AUTO_EN))
+ return -1;
+
+ return 0;
+}
+
+/* This function initializes the IO ports.
*
* The following operations are performed -
* - Read the IO ports (0, 1 and 2)
@@ -409,10 +460,17 @@ static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
*/
static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter)
{
- u32 reg;
+ u8 reg;
+ struct sdio_mmc_card *card = adapter->card;
adapter->ioport = 0;
+ if (card->supports_sdio_new_mode) {
+ if (mwifiex_init_sdio_new_mode(adapter))
+ return -1;
+ goto cont;
+ }
+
/* Read the IO port */
if (!mwifiex_read_reg(adapter, IO_PORT_0_REG, &reg))
adapter->ioport |= (reg & 0xff);
@@ -428,19 +486,19 @@ static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter)
adapter->ioport |= ((reg & 0xff) << 16);
else
return -1;
-
+cont:
pr_debug("info: SDIO FUNC1 IO port: %#x\n", adapter->ioport);
/* Set Host interrupt reset to read to clear */
if (!mwifiex_read_reg(adapter, HOST_INT_RSR_REG, &reg))
mwifiex_write_reg(adapter, HOST_INT_RSR_REG,
- reg | SDIO_INT_MASK);
+ reg | card->reg->sdio_int_mask);
else
return -1;
/* Dnld/Upld ready set to auto reset */
- if (!mwifiex_read_reg(adapter, CARD_MISC_CFG_REG, &reg))
- mwifiex_write_reg(adapter, CARD_MISC_CFG_REG,
+ if (!mwifiex_read_reg(adapter, card->reg->card_misc_cfg_reg, &reg))
+ mwifiex_write_reg(adapter, card->reg->card_misc_cfg_reg,
reg | AUTO_RE_ENABLE_INT);
else
return -1;
@@ -486,34 +544,42 @@ static int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter,
static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port)
{
struct sdio_mmc_card *card = adapter->card;
- u16 rd_bitmap = card->mp_rd_bitmap;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
+ u32 rd_bitmap = card->mp_rd_bitmap;
- dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%04x\n", rd_bitmap);
+ dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%08x\n", rd_bitmap);
- if (!(rd_bitmap & (CTRL_PORT_MASK | DATA_PORT_MASK)))
- return -1;
+ if (card->supports_sdio_new_mode) {
+ if (!(rd_bitmap & reg->data_port_mask))
+ return -1;
+ } else {
+ if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask)))
+ return -1;
+ }
- if (card->mp_rd_bitmap & CTRL_PORT_MASK) {
- card->mp_rd_bitmap &= (u16) (~CTRL_PORT_MASK);
+ if ((card->has_control_mask) &&
+ (card->mp_rd_bitmap & CTRL_PORT_MASK)) {
+ card->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK);
*port = CTRL_PORT;
- dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%04x\n",
+ dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%08x\n",
*port, card->mp_rd_bitmap);
- } else {
- if (card->mp_rd_bitmap & (1 << card->curr_rd_port)) {
- card->mp_rd_bitmap &= (u16)
- (~(1 << card->curr_rd_port));
- *port = card->curr_rd_port;
+ return 0;
+ }
- if (++card->curr_rd_port == MAX_PORT)
- card->curr_rd_port = 1;
- } else {
- return -1;
- }
+ if (!(card->mp_rd_bitmap & (1 << card->curr_rd_port)))
+ return -1;
+
+ /* We are now handling the SDIO data ports */
+ card->mp_rd_bitmap &= (u32)(~(1 << card->curr_rd_port));
+ *port = card->curr_rd_port;
+
+ if (++card->curr_rd_port == card->max_ports)
+ card->curr_rd_port = reg->start_rd_port;
+
+ dev_dbg(adapter->dev,
+ "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n",
+ *port, rd_bitmap, card->mp_rd_bitmap);
- dev_dbg(adapter->dev,
- "data: port=%d mp_rd_bitmap=0x%04x -> 0x%04x\n",
- *port, rd_bitmap, card->mp_rd_bitmap);
- }
return 0;
}
@@ -524,35 +590,45 @@ static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port)
* increased (provided it does not reach the maximum limit, in which
* case it is reset to 1)
*/
-static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port)
+static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u32 *port)
{
struct sdio_mmc_card *card = adapter->card;
- u16 wr_bitmap = card->mp_wr_bitmap;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
+ u32 wr_bitmap = card->mp_wr_bitmap;
- dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%04x\n", wr_bitmap);
+ dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%08x\n", wr_bitmap);
- if (!(wr_bitmap & card->mp_data_port_mask))
+ if (card->supports_sdio_new_mode &&
+ !(wr_bitmap & reg->data_port_mask)) {
+ adapter->data_sent = true;
+ return -EBUSY;
+ } else if (!card->supports_sdio_new_mode &&
+ !(wr_bitmap & card->mp_data_port_mask)) {
return -1;
+ }
if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) {
- card->mp_wr_bitmap &= (u16) (~(1 << card->curr_wr_port));
+ card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port));
*port = card->curr_wr_port;
- if (++card->curr_wr_port == card->mp_end_port)
- card->curr_wr_port = 1;
+ if (((card->supports_sdio_new_mode) &&
+ (++card->curr_wr_port == card->max_ports)) ||
+ ((!card->supports_sdio_new_mode) &&
+ (++card->curr_wr_port == card->mp_end_port)))
+ card->curr_wr_port = reg->start_wr_port;
} else {
adapter->data_sent = true;
return -EBUSY;
}
- if (*port == CTRL_PORT) {
- dev_err(adapter->dev, "invalid data port=%d cur port=%d"
- " mp_wr_bitmap=0x%04x -> 0x%04x\n",
+ if ((card->has_control_mask) && (*port == CTRL_PORT)) {
+ dev_err(adapter->dev,
+ "invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n",
*port, card->curr_wr_port, wr_bitmap,
card->mp_wr_bitmap);
return -1;
}
- dev_dbg(adapter->dev, "data: port=%d mp_wr_bitmap=0x%04x -> 0x%04x\n",
+ dev_dbg(adapter->dev, "data: port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n",
*port, wr_bitmap, card->mp_wr_bitmap);
return 0;
@@ -564,11 +640,12 @@ static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port)
static int
mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits)
{
+ struct sdio_mmc_card *card = adapter->card;
u32 tries;
- u32 cs;
+ u8 cs;
for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
- if (mwifiex_read_reg(adapter, CARD_STATUS_REG, &cs))
+ if (mwifiex_read_reg(adapter, card->reg->poll_reg, &cs))
break;
else if ((cs & bits) == bits)
return 0;
@@ -587,12 +664,14 @@ mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits)
static int
mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat)
{
- u32 fws0, fws1;
+ struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
+ u8 fws0, fws1;
- if (mwifiex_read_reg(adapter, CARD_FW_STATUS0_REG, &fws0))
+ if (mwifiex_read_reg(adapter, reg->status_reg_0, &fws0))
return -1;
- if (mwifiex_read_reg(adapter, CARD_FW_STATUS1_REG, &fws1))
+ if (mwifiex_read_reg(adapter, reg->status_reg_1, &fws1))
return -1;
*dat = (u16) ((fws1 << 8) | fws0);
@@ -608,14 +687,14 @@ mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat)
*/
static int mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter)
{
- u32 host_int_mask;
+ u8 host_int_mask, host_int_disable = HOST_INT_DISABLE;
/* Read back the host_int_mask register */
if (mwifiex_read_reg(adapter, HOST_INT_MASK_REG, &host_int_mask))
return -1;
/* Update with the mask and write back to the register */
- host_int_mask &= ~HOST_INT_DISABLE;
+ host_int_mask &= ~host_int_disable;
if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, host_int_mask)) {
dev_err(adapter->dev, "disable host interrupt failed\n");
@@ -633,8 +712,11 @@ static int mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter)
*/
static int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter)
{
+ struct sdio_mmc_card *card = adapter->card;
+
/* Simply write the mask to the register */
- if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, HOST_INT_ENABLE)) {
+ if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG,
+ card->reg->host_int_enable)) {
dev_err(adapter->dev, "enable host interrupt failed\n");
return -1;
}
@@ -686,11 +768,13 @@ static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter,
static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
struct mwifiex_fw_image *fw)
{
+ struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
int ret;
u8 *firmware = fw->fw_buf;
u32 firmware_len = fw->fw_len;
u32 offset = 0;
- u32 base0, base1;
+ u8 base0, base1;
u8 *fwbuf;
u16 len = 0;
u32 txlen, tx_blocks = 0, tries;
@@ -727,7 +811,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
break;
for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
- ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_0,
+ ret = mwifiex_read_reg(adapter, reg->base_0_reg,
&base0);
if (ret) {
dev_err(adapter->dev,
@@ -736,7 +820,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
base0, base0);
goto done;
}
- ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_1,
+ ret = mwifiex_read_reg(adapter, reg->base_1_reg,
&base1);
if (ret) {
dev_err(adapter->dev,
@@ -828,10 +912,11 @@ done:
static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
u32 poll_num)
{
+ struct sdio_mmc_card *card = adapter->card;
int ret = 0;
u16 firmware_stat;
u32 tries;
- u32 winner_status;
+ u8 winner_status;
/* Wait for firmware initialization event */
for (tries = 0; tries < poll_num; tries++) {
@@ -849,7 +934,7 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
if (ret) {
if (mwifiex_read_reg
- (adapter, CARD_FW_STATUS0_REG, &winner_status))
+ (adapter, card->reg->status_reg_0, &winner_status))
winner_status = 0;
if (winner_status)
@@ -866,12 +951,12 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
{
struct sdio_mmc_card *card = adapter->card;
- u32 sdio_ireg;
+ u8 sdio_ireg;
unsigned long flags;
- if (mwifiex_read_data_sync(adapter, card->mp_regs, MAX_MP_REGS,
- REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK,
- 0)) {
+ if (mwifiex_read_data_sync(adapter, card->mp_regs,
+ card->reg->max_mp_regs,
+ REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) {
dev_err(adapter->dev, "read mp_regs failed\n");
return;
}
@@ -880,6 +965,9 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
if (sdio_ireg) {
/*
* DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+ * For SDIO new mode CMD port interrupts
+ * DN_LD_CMD_PORT_HOST_INT_STATUS and/or
+ * UP_LD_CMD_PORT_HOST_INT_STATUS
* Clear the interrupt status register
*/
dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg);
@@ -1003,11 +1091,11 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
s32 f_aggr_cur = 0;
struct sk_buff *skb_deaggr;
u32 pind;
- u32 pkt_len, pkt_type = 0;
+ u32 pkt_len, pkt_type, mport;
u8 *curr_ptr;
u32 rx_len = skb->len;
- if (port == CTRL_PORT) {
+ if ((card->has_control_mask) && (port == CTRL_PORT)) {
/* Read the command Resp without aggr */
dev_dbg(adapter->dev, "info: %s: no aggregation for cmd "
"response\n", __func__);
@@ -1024,7 +1112,10 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
goto rx_curr_single;
}
- if (card->mp_rd_bitmap & (~((u16) CTRL_PORT_MASK))) {
+ if ((!card->has_control_mask && (card->mp_rd_bitmap &
+ card->reg->data_port_mask)) ||
+ (card->has_control_mask && (card->mp_rd_bitmap &
+ (~((u32) CTRL_PORT_MASK))))) {
/* Some more data RX pending */
dev_dbg(adapter->dev, "info: %s: not last packet\n", __func__);
@@ -1060,10 +1151,10 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
if (f_aggr_cur) {
dev_dbg(adapter->dev, "info: current packet aggregation\n");
/* Curr pkt can be aggregated */
- MP_RX_AGGR_SETUP(card, skb, port);
+ mp_rx_aggr_setup(card, skb, port);
if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) ||
- MP_RX_AGGR_PORT_LIMIT_REACHED(card)) {
+ mp_rx_aggr_port_limit_reached(card)) {
dev_dbg(adapter->dev, "info: %s: aggregated packet "
"limit reached\n", __func__);
/* No more pkts allowed in Aggr buf, rx it */
@@ -1076,11 +1167,28 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
dev_dbg(adapter->dev, "info: do_rx_aggr: num of packets: %d\n",
card->mpa_rx.pkt_cnt);
+ if (card->supports_sdio_new_mode) {
+ int i;
+ u32 port_count;
+
+ for (i = 0, port_count = 0; i < card->max_ports; i++)
+ if (card->mpa_rx.ports & BIT(i))
+ port_count++;
+
+ /* Reading data from "start_port + 0" to "start_port +
+ * port_count -1", so decrease the count by 1
+ */
+ port_count--;
+ mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+ (port_count << 8)) + card->mpa_rx.start_port;
+ } else {
+ mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+ (card->mpa_rx.ports << 4)) +
+ card->mpa_rx.start_port;
+ }
+
if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf,
- card->mpa_rx.buf_len,
- (adapter->ioport | 0x1000 |
- (card->mpa_rx.ports << 4)) +
- card->mpa_rx.start_port, 1))
+ card->mpa_rx.buf_len, mport, 1))
goto error;
curr_ptr = card->mpa_rx.buf;
@@ -1167,6 +1275,7 @@ error:
static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
{
struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
int ret = 0;
u8 sdio_ireg;
struct sk_buff *skb;
@@ -1175,6 +1284,8 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
u32 rx_blocks;
u16 rx_len;
unsigned long flags;
+ u32 bitmap;
+ u8 cr;
spin_lock_irqsave(&adapter->int_lock, flags);
sdio_ireg = adapter->int_status;
@@ -1184,10 +1295,60 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
if (!sdio_ireg)
return ret;
+ /* Following interrupt is only for SDIO new mode */
+ if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent)
+ adapter->cmd_sent = false;
+
+ /* Following interrupt is only for SDIO new mode */
+ if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) {
+ u32 pkt_type;
+
+ /* read the len of control packet */
+ rx_len = card->mp_regs[CMD_RD_LEN_1] << 8;
+ rx_len |= (u16) card->mp_regs[CMD_RD_LEN_0];
+ rx_blocks = DIV_ROUND_UP(rx_len, MWIFIEX_SDIO_BLOCK_SIZE);
+ if (rx_len <= INTF_HEADER_LEN ||
+ (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) >
+ MWIFIEX_RX_DATA_BUF_SIZE)
+ return -1;
+ rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE);
+
+ skb = dev_alloc_skb(rx_len);
+ if (!skb)
+ return -1;
+
+ skb_put(skb, rx_len);
+
+ if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data,
+ skb->len, adapter->ioport |
+ CMD_PORT_SLCT)) {
+ dev_err(adapter->dev,
+ "%s: failed to card_to_host", __func__);
+ dev_kfree_skb_any(skb);
+ goto term_cmd;
+ }
+
+ if ((pkt_type != MWIFIEX_TYPE_CMD) &&
+ (pkt_type != MWIFIEX_TYPE_EVENT))
+ dev_err(adapter->dev,
+ "%s:Received wrong packet on cmd port",
+ __func__);
+
+ mwifiex_decode_rx_packet(adapter, skb, pkt_type);
+ }
+
if (sdio_ireg & DN_LD_HOST_INT_STATUS) {
- card->mp_wr_bitmap = ((u16) card->mp_regs[WR_BITMAP_U]) << 8;
- card->mp_wr_bitmap |= (u16) card->mp_regs[WR_BITMAP_L];
- dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%04x\n",
+ bitmap = (u32) card->mp_regs[reg->wr_bitmap_l];
+ bitmap |= ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8;
+ if (card->supports_sdio_new_mode) {
+ bitmap |=
+ ((u32) card->mp_regs[reg->wr_bitmap_1l]) << 16;
+ bitmap |=
+ ((u32) card->mp_regs[reg->wr_bitmap_1u]) << 24;
+ }
+ card->mp_wr_bitmap = bitmap;
+
+ dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%x\n",
card->mp_wr_bitmap);
if (adapter->data_sent &&
(card->mp_wr_bitmap & card->mp_data_port_mask)) {
@@ -1200,11 +1361,11 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
/* As firmware will not generate download ready interrupt if the port
updated is command port only, cmd_sent should be done for any SDIO
interrupt. */
- if (adapter->cmd_sent) {
+ if (card->has_control_mask && adapter->cmd_sent) {
/* Check if firmware has attach buffer at command port and
update just that in wr_bit_map. */
card->mp_wr_bitmap |=
- (u16) card->mp_regs[WR_BITMAP_L] & CTRL_PORT_MASK;
+ (u32) card->mp_regs[reg->wr_bitmap_l] & CTRL_PORT_MASK;
if (card->mp_wr_bitmap & CTRL_PORT_MASK)
adapter->cmd_sent = false;
}
@@ -1212,9 +1373,16 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
dev_dbg(adapter->dev, "info: cmd_sent=%d data_sent=%d\n",
adapter->cmd_sent, adapter->data_sent);
if (sdio_ireg & UP_LD_HOST_INT_STATUS) {
- card->mp_rd_bitmap = ((u16) card->mp_regs[RD_BITMAP_U]) << 8;
- card->mp_rd_bitmap |= (u16) card->mp_regs[RD_BITMAP_L];
- dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%04x\n",
+ bitmap = (u32) card->mp_regs[reg->rd_bitmap_l];
+ bitmap |= ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8;
+ if (card->supports_sdio_new_mode) {
+ bitmap |=
+ ((u32) card->mp_regs[reg->rd_bitmap_1l]) << 16;
+ bitmap |=
+ ((u32) card->mp_regs[reg->rd_bitmap_1u]) << 24;
+ }
+ card->mp_rd_bitmap = bitmap;
+ dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%x\n",
card->mp_rd_bitmap);
while (true) {
@@ -1224,8 +1392,8 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
"info: no more rd_port available\n");
break;
}
- len_reg_l = RD_LEN_P0_L + (port << 1);
- len_reg_u = RD_LEN_P0_U + (port << 1);
+ len_reg_l = reg->rd_len_p0_l + (port << 1);
+ len_reg_u = reg->rd_len_p0_u + (port << 1);
rx_len = ((u16) card->mp_regs[len_reg_u]) << 8;
rx_len |= (u16) card->mp_regs[len_reg_l];
dev_dbg(adapter->dev, "info: RX: port=%d rx_len=%u\n",
@@ -1257,37 +1425,33 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
if (mwifiex_sdio_card_to_host_mp_aggr(adapter, skb,
port)) {
- u32 cr = 0;
-
dev_err(adapter->dev, "card_to_host_mpa failed:"
" int status=%#x\n", sdio_ireg);
- if (mwifiex_read_reg(adapter,
- CONFIGURATION_REG, &cr))
- dev_err(adapter->dev,
- "read CFG reg failed\n");
-
- dev_dbg(adapter->dev,
- "info: CFG reg val = %d\n", cr);
- if (mwifiex_write_reg(adapter,
- CONFIGURATION_REG,
- (cr | 0x04)))
- dev_err(adapter->dev,
- "write CFG reg failed\n");
-
- dev_dbg(adapter->dev, "info: write success\n");
- if (mwifiex_read_reg(adapter,
- CONFIGURATION_REG, &cr))
- dev_err(adapter->dev,
- "read CFG reg failed\n");
-
- dev_dbg(adapter->dev,
- "info: CFG reg val =%x\n", cr);
- return -1;
+ goto term_cmd;
}
}
}
return 0;
+
+term_cmd:
+ /* terminate cmd */
+ if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr))
+ dev_err(adapter->dev, "read CFG reg failed\n");
+ else
+ dev_dbg(adapter->dev, "info: CFG reg val = %d\n", cr);
+
+ if (mwifiex_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04)))
+ dev_err(adapter->dev, "write CFG reg failed\n");
+ else
+ dev_dbg(adapter->dev, "info: write success\n");
+
+ if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr))
+ dev_err(adapter->dev, "read CFG reg failed\n");
+ else
+ dev_dbg(adapter->dev, "info: CFG reg val =%x\n", cr);
+
+ return -1;
}
/*
@@ -1305,7 +1469,7 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
* and return.
*/
static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
- u8 *payload, u32 pkt_len, u8 port,
+ u8 *payload, u32 pkt_len, u32 port,
u32 next_pkt_len)
{
struct sdio_mmc_card *card = adapter->card;
@@ -1314,8 +1478,11 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
s32 f_send_cur_buf = 0;
s32 f_precopy_cur_buf = 0;
s32 f_postcopy_cur_buf = 0;
+ u32 mport;
- if ((!card->mpa_tx.enabled) || (port == CTRL_PORT)) {
+ if (!card->mpa_tx.enabled ||
+ (card->has_control_mask && (port == CTRL_PORT)) ||
+ (card->supports_sdio_new_mode && (port == CMD_PORT_SLCT))) {
dev_dbg(adapter->dev, "info: %s: tx aggregation disabled\n",
__func__);
@@ -1329,7 +1496,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
__func__);
if (MP_TX_AGGR_IN_PROGRESS(card)) {
- if (!MP_TX_AGGR_PORT_LIMIT_REACHED(card) &&
+ if (!mp_tx_aggr_port_limit_reached(card) &&
MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) {
f_precopy_cur_buf = 1;
@@ -1342,7 +1509,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
/* No room in Aggr buf, send it */
f_send_aggr_buf = 1;
- if (MP_TX_AGGR_PORT_LIMIT_REACHED(card) ||
+ if (mp_tx_aggr_port_limit_reached(card) ||
!(card->mp_wr_bitmap &
(1 << card->curr_wr_port)))
f_send_cur_buf = 1;
@@ -1381,7 +1548,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port);
if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) ||
- MP_TX_AGGR_PORT_LIMIT_REACHED(card))
+ mp_tx_aggr_port_limit_reached(card))
/* No more pkts allowed in Aggr buf, send it */
f_send_aggr_buf = 1;
}
@@ -1390,11 +1557,28 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
dev_dbg(adapter->dev, "data: %s: send aggr buffer: %d %d\n",
__func__,
card->mpa_tx.start_port, card->mpa_tx.ports);
+ if (card->supports_sdio_new_mode) {
+ u32 port_count;
+ int i;
+
+ for (i = 0, port_count = 0; i < card->max_ports; i++)
+ if (card->mpa_tx.ports & BIT(i))
+ port_count++;
+
+ /* Writing data from "start_port + 0" to "start_port +
+ * port_count -1", so decrease the count by 1
+ */
+ port_count--;
+ mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+ (port_count << 8)) + card->mpa_tx.start_port;
+ } else {
+ mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+ (card->mpa_tx.ports << 4)) +
+ card->mpa_tx.start_port;
+ }
+
ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf,
- card->mpa_tx.buf_len,
- (adapter->ioport | 0x1000 |
- (card->mpa_tx.ports << 4)) +
- card->mpa_tx.start_port);
+ card->mpa_tx.buf_len, mport);
MP_TX_AGGR_BUF_RESET(card);
}
@@ -1434,7 +1618,7 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter,
int ret;
u32 buf_block_len;
u32 blk_size;
- u8 port = CTRL_PORT;
+ u32 port = CTRL_PORT;
u8 *payload = (u8 *)skb->data;
u32 pkt_len = skb->len;
@@ -1465,6 +1649,9 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter,
pkt_len > MWIFIEX_UPLD_SIZE)
dev_err(adapter->dev, "%s: payload=%p, nb=%d\n",
__func__, payload, pkt_len);
+
+ if (card->supports_sdio_new_mode)
+ port = CMD_PORT_SLCT;
}
/* Transfer data to card */
@@ -1586,18 +1773,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
adapter->dev = &func->dev;
- switch (func->device) {
- case SDIO_DEVICE_ID_MARVELL_8786:
- strcpy(adapter->fw_name, SD8786_DEFAULT_FW_NAME);
- break;
- case SDIO_DEVICE_ID_MARVELL_8797:
- strcpy(adapter->fw_name, SD8797_DEFAULT_FW_NAME);
- break;
- case SDIO_DEVICE_ID_MARVELL_8787:
- default:
- strcpy(adapter->fw_name, SD8787_DEFAULT_FW_NAME);
- break;
- }
+ strcpy(adapter->fw_name, card->firmware);
return 0;
@@ -1626,8 +1802,9 @@ disable_func:
static int mwifiex_init_sdio(struct mwifiex_adapter *adapter)
{
struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
int ret;
- u32 sdio_ireg;
+ u8 sdio_ireg;
/*
* Read the HOST_INT_STATUS_REG for ACK the first interrupt got
@@ -1645,30 +1822,35 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter)
/* Initialize SDIO variables in card */
card->mp_rd_bitmap = 0;
card->mp_wr_bitmap = 0;
- card->curr_rd_port = 1;
- card->curr_wr_port = 1;
+ card->curr_rd_port = reg->start_rd_port;
+ card->curr_wr_port = reg->start_wr_port;
- card->mp_data_port_mask = DATA_PORT_MASK;
+ card->mp_data_port_mask = reg->data_port_mask;
card->mpa_tx.buf_len = 0;
card->mpa_tx.pkt_cnt = 0;
card->mpa_tx.start_port = 0;
card->mpa_tx.enabled = 1;
- card->mpa_tx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT;
+ card->mpa_tx.pkt_aggr_limit = card->mp_agg_pkt_limit;
card->mpa_rx.buf_len = 0;
card->mpa_rx.pkt_cnt = 0;
card->mpa_rx.start_port = 0;
card->mpa_rx.enabled = 1;
- card->mpa_rx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT;
+ card->mpa_rx.pkt_aggr_limit = card->mp_agg_pkt_limit;
/* Allocate buffers for SDIO MP-A */
- card->mp_regs = kzalloc(MAX_MP_REGS, GFP_KERNEL);
+ card->mp_regs = kzalloc(reg->max_mp_regs, GFP_KERNEL);
if (!card->mp_regs)
return -ENOMEM;
+ /* Allocate skb pointer buffers */
+ card->mpa_rx.skb_arr = kzalloc((sizeof(void *)) *
+ card->mp_agg_pkt_limit, GFP_KERNEL);
+ card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) *
+ card->mp_agg_pkt_limit, GFP_KERNEL);
ret = mwifiex_alloc_sdio_mpa_buffers(adapter,
SDIO_MP_TX_AGGR_DEF_BUF_SIZE,
SDIO_MP_RX_AGGR_DEF_BUF_SIZE);
@@ -1705,6 +1887,8 @@ static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter)
struct sdio_mmc_card *card = adapter->card;
kfree(card->mp_regs);
+ kfree(card->mpa_rx.skb_arr);
+ kfree(card->mpa_rx.len_arr);
kfree(card->mpa_tx.buf);
kfree(card->mpa_rx.buf);
}
@@ -1716,16 +1900,20 @@ static void
mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port)
{
struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
int i;
card->mp_end_port = port;
- card->mp_data_port_mask = DATA_PORT_MASK;
+ card->mp_data_port_mask = reg->data_port_mask;
- for (i = 1; i <= MAX_PORT - card->mp_end_port; i++)
- card->mp_data_port_mask &= ~(1 << (MAX_PORT - i));
+ if (reg->start_wr_port) {
+ for (i = 1; i <= card->max_ports - card->mp_end_port; i++)
+ card->mp_data_port_mask &=
+ ~(1 << (card->max_ports - i));
+ }
- card->curr_wr_port = 1;
+ card->curr_wr_port = reg->start_wr_port;
dev_dbg(adapter->dev, "cmd: mp_end_port %d, data port mask 0x%x\n",
port, card->mp_data_port_mask);
@@ -1831,3 +2019,4 @@ MODULE_LICENSE("GPL v2");
MODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME);
MODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME);
MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME);
+MODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME);
diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h
index 8cc5468654b4..6d51dfdd8251 100644
--- a/drivers/net/wireless/mwifiex/sdio.h
+++ b/drivers/net/wireless/mwifiex/sdio.h
@@ -32,30 +32,37 @@
#define SD8786_DEFAULT_FW_NAME "mrvl/sd8786_uapsta.bin"
#define SD8787_DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin"
#define SD8797_DEFAULT_FW_NAME "mrvl/sd8797_uapsta.bin"
+#define SD8897_DEFAULT_FW_NAME "mrvl/sd8897_uapsta.bin"
#define BLOCK_MODE 1
#define BYTE_MODE 0
#define REG_PORT 0
-#define RD_BITMAP_L 0x04
-#define RD_BITMAP_U 0x05
-#define WR_BITMAP_L 0x06
-#define WR_BITMAP_U 0x07
-#define RD_LEN_P0_L 0x08
-#define RD_LEN_P0_U 0x09
#define MWIFIEX_SDIO_IO_PORT_MASK 0xfffff
#define MWIFIEX_SDIO_BYTE_MODE_MASK 0x80000000
+#define SDIO_MPA_ADDR_BASE 0x1000
#define CTRL_PORT 0
#define CTRL_PORT_MASK 0x0001
-#define DATA_PORT_MASK 0xfffe
-#define MAX_MP_REGS 64
-#define MAX_PORT 16
-
-#define SDIO_MP_AGGR_DEF_PKT_LIMIT 8
+#define CMD_PORT_UPLD_INT_MASK (0x1U<<6)
+#define CMD_PORT_DNLD_INT_MASK (0x1U<<7)
+#define HOST_TERM_CMD53 (0x1U << 2)
+#define REG_PORT 0
+#define MEM_PORT 0x10000
+#define CMD_RD_LEN_0 0xB4
+#define CMD_RD_LEN_1 0xB5
+#define CARD_CONFIG_2_1_REG 0xCD
+#define CMD53_NEW_MODE (0x1U << 0)
+#define CMD_CONFIG_0 0xB8
+#define CMD_PORT_RD_LEN_EN (0x1U << 2)
+#define CMD_CONFIG_1 0xB9
+#define CMD_PORT_AUTO_EN (0x1U << 0)
+#define CMD_PORT_SLCT 0x8000
+#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U)
+#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U)
#define SDIO_MP_TX_AGGR_DEF_BUF_SIZE (8192) /* 8K */
@@ -75,14 +82,8 @@
/* Host Control Registers : Configuration */
#define CONFIGURATION_REG 0x00
-/* Host Control Registers : Host without Command 53 finish host*/
-#define HOST_TO_CARD_EVENT (0x1U << 3)
-/* Host Control Registers : Host without Command 53 finish host */
-#define HOST_WO_CMD53_FINISH_HOST (0x1U << 2)
/* Host Control Registers : Host power up */
#define HOST_POWER_UP (0x1U << 1)
-/* Host Control Registers : Host power down */
-#define HOST_POWER_DOWN (0x1U << 0)
/* Host Control Registers : Host interrupt mask */
#define HOST_INT_MASK_REG 0x02
@@ -90,8 +91,7 @@
#define UP_LD_HOST_INT_MASK (0x1U)
/* Host Control Registers : Download host interrupt mask */
#define DN_LD_HOST_INT_MASK (0x2U)
-/* Enable Host interrupt mask */
-#define HOST_INT_ENABLE (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK)
+
/* Disable Host interrupt mask */
#define HOST_INT_DISABLE 0xff
@@ -104,74 +104,15 @@
/* Host Control Registers : Host interrupt RSR */
#define HOST_INT_RSR_REG 0x01
-/* Host Control Registers : Upload host interrupt RSR */
-#define UP_LD_HOST_INT_RSR (0x1U)
-#define SDIO_INT_MASK 0x3F
/* Host Control Registers : Host interrupt status */
#define HOST_INT_STATUS_REG 0x28
-/* Host Control Registers : Upload CRC error */
-#define UP_LD_CRC_ERR (0x1U << 2)
-/* Host Control Registers : Upload restart */
-#define UP_LD_RESTART (0x1U << 1)
-/* Host Control Registers : Download restart */
-#define DN_LD_RESTART (0x1U << 0)
-
-/* Card Control Registers : Card status register */
-#define CARD_STATUS_REG 0x30
+
/* Card Control Registers : Card I/O ready */
#define CARD_IO_READY (0x1U << 3)
-/* Card Control Registers : CIS card ready */
-#define CIS_CARD_RDY (0x1U << 2)
-/* Card Control Registers : Upload card ready */
-#define UP_LD_CARD_RDY (0x1U << 1)
/* Card Control Registers : Download card ready */
#define DN_LD_CARD_RDY (0x1U << 0)
-/* Card Control Registers : Host interrupt mask register */
-#define HOST_INTERRUPT_MASK_REG 0x34
-/* Card Control Registers : Host power interrupt mask */
-#define HOST_POWER_INT_MASK (0x1U << 3)
-/* Card Control Registers : Abort card interrupt mask */
-#define ABORT_CARD_INT_MASK (0x1U << 2)
-/* Card Control Registers : Upload card interrupt mask */
-#define UP_LD_CARD_INT_MASK (0x1U << 1)
-/* Card Control Registers : Download card interrupt mask */
-#define DN_LD_CARD_INT_MASK (0x1U << 0)
-
-/* Card Control Registers : Card interrupt status register */
-#define CARD_INTERRUPT_STATUS_REG 0x38
-/* Card Control Registers : Power up interrupt */
-#define POWER_UP_INT (0x1U << 4)
-/* Card Control Registers : Power down interrupt */
-#define POWER_DOWN_INT (0x1U << 3)
-
-/* Card Control Registers : Card interrupt RSR register */
-#define CARD_INTERRUPT_RSR_REG 0x3c
-/* Card Control Registers : Power up RSR */
-#define POWER_UP_RSR (0x1U << 4)
-/* Card Control Registers : Power down RSR */
-#define POWER_DOWN_RSR (0x1U << 3)
-
-/* Card Control Registers : Miscellaneous Configuration Register */
-#define CARD_MISC_CFG_REG 0x6C
-
-/* Host F1 read base 0 */
-#define HOST_F1_RD_BASE_0 0x0040
-/* Host F1 read base 1 */
-#define HOST_F1_RD_BASE_1 0x0041
-/* Host F1 card ready */
-#define HOST_F1_CARD_RDY 0x0020
-
-/* Firmware status 0 register */
-#define CARD_FW_STATUS0_REG 0x60
-/* Firmware status 1 register */
-#define CARD_FW_STATUS1_REG 0x61
-/* Rx length register */
-#define CARD_RX_LEN_REG 0x62
-/* Rx unit register */
-#define CARD_RX_UNIT_REG 0x63
-
/* Max retry number of CMD53 write */
#define MAX_WRITE_IOMEM_RETRY 2
@@ -192,7 +133,8 @@
if (a->mpa_tx.start_port <= port) \
a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt)); \
else \
- a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+(MAX_PORT - \
+ a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+ \
+ (a->max_ports - \
a->mp_end_port))); \
a->mpa_tx.pkt_cnt++; \
} while (0)
@@ -201,12 +143,6 @@
#define MP_TX_AGGR_PKT_LIMIT_REACHED(a) \
(a->mpa_tx.pkt_cnt == a->mpa_tx.pkt_aggr_limit)
-/* SDIO Tx aggregation port limit ? */
-#define MP_TX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_wr_port < \
- a->mpa_tx.start_port) && (((MAX_PORT - \
- a->mpa_tx.start_port) + a->curr_wr_port) >= \
- SDIO_MP_AGGR_DEF_PKT_LIMIT))
-
/* Reset SDIO Tx aggregation buffer parameters */
#define MP_TX_AGGR_BUF_RESET(a) do { \
a->mpa_tx.pkt_cnt = 0; \
@@ -219,12 +155,6 @@
#define MP_RX_AGGR_PKT_LIMIT_REACHED(a) \
(a->mpa_rx.pkt_cnt == a->mpa_rx.pkt_aggr_limit)
-/* SDIO Tx aggregation port limit ? */
-#define MP_RX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_rd_port < \
- a->mpa_rx.start_port) && (((MAX_PORT - \
- a->mpa_rx.start_port) + a->curr_rd_port) >= \
- SDIO_MP_AGGR_DEF_PKT_LIMIT))
-
/* SDIO Rx aggregation in progress ? */
#define MP_RX_AGGR_IN_PROGRESS(a) (a->mpa_rx.pkt_cnt > 0)
@@ -232,20 +162,6 @@
#define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) \
((a->mpa_rx.buf_len+rx_len) <= a->mpa_rx.buf_size)
-/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */
-#define MP_RX_AGGR_SETUP(a, skb, port) do { \
- a->mpa_rx.buf_len += skb->len; \
- if (!a->mpa_rx.pkt_cnt) \
- a->mpa_rx.start_port = port; \
- if (a->mpa_rx.start_port <= port) \
- a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt)); \
- else \
- a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt+1)); \
- a->mpa_rx.skb_arr[a->mpa_rx.pkt_cnt] = skb; \
- a->mpa_rx.len_arr[a->mpa_rx.pkt_cnt] = skb->len; \
- a->mpa_rx.pkt_cnt++; \
-} while (0)
-
/* Reset SDIO Rx aggregation buffer parameters */
#define MP_RX_AGGR_BUF_RESET(a) do { \
a->mpa_rx.pkt_cnt = 0; \
@@ -254,14 +170,13 @@
a->mpa_rx.start_port = 0; \
} while (0)
-
/* data structure for SDIO MPA TX */
struct mwifiex_sdio_mpa_tx {
/* multiport tx aggregation buffer pointer */
u8 *buf;
u32 buf_len;
u32 pkt_cnt;
- u16 ports;
+ u32 ports;
u16 start_port;
u8 enabled;
u32 buf_size;
@@ -272,11 +187,11 @@ struct mwifiex_sdio_mpa_rx {
u8 *buf;
u32 buf_len;
u32 pkt_cnt;
- u16 ports;
+ u32 ports;
u16 start_port;
- struct sk_buff *skb_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT];
- u32 len_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT];
+ struct sk_buff **skb_arr;
+ u32 *len_arr;
u8 enabled;
u32 buf_size;
@@ -286,15 +201,47 @@ struct mwifiex_sdio_mpa_rx {
int mwifiex_bus_register(void);
void mwifiex_bus_unregister(void);
+struct mwifiex_sdio_card_reg {
+ u8 start_rd_port;
+ u8 start_wr_port;
+ u8 base_0_reg;
+ u8 base_1_reg;
+ u8 poll_reg;
+ u8 host_int_enable;
+ u8 status_reg_0;
+ u8 status_reg_1;
+ u8 sdio_int_mask;
+ u32 data_port_mask;
+ u8 max_mp_regs;
+ u8 rd_bitmap_l;
+ u8 rd_bitmap_u;
+ u8 rd_bitmap_1l;
+ u8 rd_bitmap_1u;
+ u8 wr_bitmap_l;
+ u8 wr_bitmap_u;
+ u8 wr_bitmap_1l;
+ u8 wr_bitmap_1u;
+ u8 rd_len_p0_l;
+ u8 rd_len_p0_u;
+ u8 card_misc_cfg_reg;
+};
+
struct sdio_mmc_card {
struct sdio_func *func;
struct mwifiex_adapter *adapter;
- u16 mp_rd_bitmap;
- u16 mp_wr_bitmap;
+ const char *firmware;
+ const struct mwifiex_sdio_card_reg *reg;
+ u8 max_ports;
+ u8 mp_agg_pkt_limit;
+ bool supports_sdio_new_mode;
+ bool has_control_mask;
+
+ u32 mp_rd_bitmap;
+ u32 mp_wr_bitmap;
u16 mp_end_port;
- u16 mp_data_port_mask;
+ u32 mp_data_port_mask;
u8 curr_rd_port;
u8 curr_wr_port;
@@ -305,6 +252,98 @@ struct sdio_mmc_card {
struct mwifiex_sdio_mpa_rx mpa_rx;
};
+struct mwifiex_sdio_device {
+ const char *firmware;
+ const struct mwifiex_sdio_card_reg *reg;
+ u8 max_ports;
+ u8 mp_agg_pkt_limit;
+ bool supports_sdio_new_mode;
+ bool has_control_mask;
+};
+
+static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = {
+ .start_rd_port = 1,
+ .start_wr_port = 1,
+ .base_0_reg = 0x0040,
+ .base_1_reg = 0x0041,
+ .poll_reg = 0x30,
+ .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK,
+ .status_reg_0 = 0x60,
+ .status_reg_1 = 0x61,
+ .sdio_int_mask = 0x3f,
+ .data_port_mask = 0x0000fffe,
+ .max_mp_regs = 64,
+ .rd_bitmap_l = 0x04,
+ .rd_bitmap_u = 0x05,
+ .wr_bitmap_l = 0x06,
+ .wr_bitmap_u = 0x07,
+ .rd_len_p0_l = 0x08,
+ .rd_len_p0_u = 0x09,
+ .card_misc_cfg_reg = 0x6c,
+};
+
+static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = {
+ .start_rd_port = 0,
+ .start_wr_port = 0,
+ .base_0_reg = 0x60,
+ .base_1_reg = 0x61,
+ .poll_reg = 0x50,
+ .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK |
+ CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK,
+ .status_reg_0 = 0xc0,
+ .status_reg_1 = 0xc1,
+ .sdio_int_mask = 0xff,
+ .data_port_mask = 0xffffffff,
+ .max_mp_regs = 184,
+ .rd_bitmap_l = 0x04,
+ .rd_bitmap_u = 0x05,
+ .rd_bitmap_1l = 0x06,
+ .rd_bitmap_1u = 0x07,
+ .wr_bitmap_l = 0x08,
+ .wr_bitmap_u = 0x09,
+ .wr_bitmap_1l = 0x0a,
+ .wr_bitmap_1u = 0x0b,
+ .rd_len_p0_l = 0x0c,
+ .rd_len_p0_u = 0x0d,
+ .card_misc_cfg_reg = 0xcc,
+};
+
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = {
+ .firmware = SD8786_DEFAULT_FW_NAME,
+ .reg = &mwifiex_reg_sd87xx,
+ .max_ports = 16,
+ .mp_agg_pkt_limit = 8,
+ .supports_sdio_new_mode = false,
+ .has_control_mask = true,
+};
+
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
+ .firmware = SD8787_DEFAULT_FW_NAME,
+ .reg = &mwifiex_reg_sd87xx,
+ .max_ports = 16,
+ .mp_agg_pkt_limit = 8,
+ .supports_sdio_new_mode = false,
+ .has_control_mask = true,
+};
+
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
+ .firmware = SD8797_DEFAULT_FW_NAME,
+ .reg = &mwifiex_reg_sd87xx,
+ .max_ports = 16,
+ .mp_agg_pkt_limit = 8,
+ .supports_sdio_new_mode = false,
+ .has_control_mask = true,
+};
+
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
+ .firmware = SD8897_DEFAULT_FW_NAME,
+ .reg = &mwifiex_reg_sd8897,
+ .max_ports = 32,
+ .mp_agg_pkt_limit = 16,
+ .supports_sdio_new_mode = true,
+ .has_control_mask = false,
+};
+
/*
* .cmdrsp_complete handler
*/
@@ -325,4 +364,77 @@ static inline int mwifiex_sdio_event_complete(struct mwifiex_adapter *adapter,
return 0;
}
+static inline bool
+mp_rx_aggr_port_limit_reached(struct sdio_mmc_card *card)
+{
+ u8 tmp;
+
+ if (card->curr_rd_port < card->mpa_rx.start_port) {
+ if (card->supports_sdio_new_mode)
+ tmp = card->mp_end_port >> 1;
+ else
+ tmp = card->mp_agg_pkt_limit;
+
+ if (((card->max_ports - card->mpa_rx.start_port) +
+ card->curr_rd_port) >= tmp)
+ return true;
+ }
+
+ if (!card->supports_sdio_new_mode)
+ return false;
+
+ if ((card->curr_rd_port - card->mpa_rx.start_port) >=
+ (card->mp_end_port >> 1))
+ return true;
+
+ return false;
+}
+
+static inline bool
+mp_tx_aggr_port_limit_reached(struct sdio_mmc_card *card)
+{
+ u16 tmp;
+
+ if (card->curr_wr_port < card->mpa_tx.start_port) {
+ if (card->supports_sdio_new_mode)
+ tmp = card->mp_end_port >> 1;
+ else
+ tmp = card->mp_agg_pkt_limit;
+
+ if (((card->max_ports - card->mpa_tx.start_port) +
+ card->curr_wr_port) >= tmp)
+ return true;
+ }
+
+ if (!card->supports_sdio_new_mode)
+ return false;
+
+ if ((card->curr_wr_port - card->mpa_tx.start_port) >=
+ (card->mp_end_port >> 1))
+ return true;
+
+ return false;
+}
+
+/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */
+static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card,
+ struct sk_buff *skb, u8 port)
+{
+ card->mpa_rx.buf_len += skb->len;
+
+ if (!card->mpa_rx.pkt_cnt)
+ card->mpa_rx.start_port = port;
+
+ if (card->supports_sdio_new_mode) {
+ card->mpa_rx.ports |= (1 << port);
+ } else {
+ if (card->mpa_rx.start_port <= port)
+ card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt);
+ else
+ card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt + 1);
+ }
+ card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = skb;
+ card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = skb->len;
+ card->mpa_rx.pkt_cnt++;
+}
#endif /* _MWIFIEX_SDIO_H */
diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c
index b193e25977d2..8ece48580642 100644
--- a/drivers/net/wireless/mwifiex/sta_cmd.c
+++ b/drivers/net/wireless/mwifiex/sta_cmd.c
@@ -1134,6 +1134,55 @@ mwifiex_cmd_mef_cfg(struct mwifiex_private *priv,
return 0;
}
+/* This function parse cal data from ASCII to hex */
+static u32 mwifiex_parse_cal_cfg(u8 *src, size_t len, u8 *dst)
+{
+ u8 *s = src, *d = dst;
+
+ while (s - src < len) {
+ if (*s && (isspace(*s) || *s == '\t')) {
+ s++;
+ continue;
+ }
+ if (isxdigit(*s)) {
+ *d++ = simple_strtol(s, NULL, 16);
+ s += 2;
+ } else {
+ s++;
+ }
+ }
+
+ return d - dst;
+}
+
+/* This function prepares command of set_cfg_data. */
+static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_action)
+{
+ struct host_cmd_ds_802_11_cfg_data *cfg_data = &cmd->params.cfg_data;
+ struct mwifiex_adapter *adapter = priv->adapter;
+ u32 len, cal_data_offset;
+ u8 *tmp_cmd = (u8 *)cmd;
+
+ cal_data_offset = S_DS_GEN + sizeof(*cfg_data);
+ if ((adapter->cal_data->data) && (adapter->cal_data->size > 0))
+ len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data,
+ adapter->cal_data->size,
+ (u8 *)(tmp_cmd + cal_data_offset));
+ else
+ return -1;
+
+ cfg_data->action = cpu_to_le16(cmd_action);
+ cfg_data->type = cpu_to_le16(CFG_DATA_TYPE_CAL);
+ cfg_data->data_len = cpu_to_le16(len);
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA);
+ cmd->size = cpu_to_le16(S_DS_GEN + sizeof(*cfg_data) + len);
+
+ return 0;
+}
+
/*
* This function prepares the commands before sending them to the firmware.
*
@@ -1152,6 +1201,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
case HostCmd_CMD_GET_HW_SPEC:
ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr);
break;
+ case HostCmd_CMD_CFG_DATA:
+ ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, cmd_action);
+ break;
case HostCmd_CMD_MAC_CONTROL:
ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action,
data_buf);
@@ -1384,6 +1436,7 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
*/
int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
{
+ struct mwifiex_adapter *adapter = priv->adapter;
int ret;
u16 enable = true;
struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl;
@@ -1404,6 +1457,15 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
HostCmd_ACT_GEN_SET, 0, NULL);
if (ret)
return -1;
+
+ /* Download calibration data to firmware */
+ if (adapter->cal_data) {
+ ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_CFG_DATA,
+ HostCmd_ACT_GEN_SET, 0, NULL);
+ if (ret)
+ return -1;
+ }
+
/* Read MAC address from HW */
ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_GET_HW_SPEC,
HostCmd_ACT_GEN_GET, 0, NULL);
diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c
index 9f990e14966e..d85df158cc6c 100644
--- a/drivers/net/wireless/mwifiex/sta_cmdresp.c
+++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c
@@ -818,6 +818,18 @@ static int mwifiex_ret_subsc_evt(struct mwifiex_private *priv,
return 0;
}
+/* This function handles the command response of set_cfg_data */
+static int mwifiex_ret_cfg_data(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ if (resp->result != HostCmd_RESULT_OK) {
+ dev_err(priv->adapter->dev, "Cal data cmd resp failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
/*
* This function handles the command responses.
*
@@ -841,6 +853,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
case HostCmd_CMD_GET_HW_SPEC:
ret = mwifiex_ret_get_hw_spec(priv, resp);
break;
+ case HostCmd_CMD_CFG_DATA:
+ ret = mwifiex_ret_cfg_data(priv, resp);
+ break;
case HostCmd_CMD_MAC_CONTROL:
break;
case HostCmd_CMD_802_11_MAC_ADDRESS:
@@ -978,6 +993,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
case HostCmd_CMD_UAP_BSS_STOP:
priv->bss_started = 0;
break;
+ case HostCmd_CMD_UAP_STA_DEAUTH:
+ break;
case HostCmd_CMD_MEF_CFG:
break;
default:
diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c
index b04b1db29100..2de882dead0f 100644
--- a/drivers/net/wireless/mwifiex/uap_cmd.c
+++ b/drivers/net/wireless/mwifiex/uap_cmd.c
@@ -689,6 +689,23 @@ mwifiex_cmd_uap_sys_config(struct host_cmd_ds_command *cmd, u16 cmd_action,
return 0;
}
+/* This function prepares AP specific deauth command with mac supplied in
+ * function parameter.
+ */
+static int mwifiex_cmd_uap_sta_deauth(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd, u8 *mac)
+{
+ struct host_cmd_ds_sta_deauth *sta_deauth = &cmd->params.sta_deauth;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_UAP_STA_DEAUTH);
+ memcpy(sta_deauth->mac, mac, ETH_ALEN);
+ sta_deauth->reason = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING);
+
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_sta_deauth) +
+ S_DS_GEN);
+ return 0;
+}
+
/* This function prepares the AP specific commands before sending them
* to the firmware.
* This is a generic function which calls specific command preparation
@@ -710,6 +727,10 @@ int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no,
cmd->command = cpu_to_le16(cmd_no);
cmd->size = cpu_to_le16(S_DS_GEN);
break;
+ case HostCmd_CMD_UAP_STA_DEAUTH:
+ if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf))
+ return -1;
+ break;
default:
dev_err(priv->adapter->dev,
"PREP_CMD: unknown cmd %#x\n", cmd_no);
diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c
index 21c640d3b579..718066577c6c 100644
--- a/drivers/net/wireless/mwifiex/uap_event.c
+++ b/drivers/net/wireless/mwifiex/uap_event.c
@@ -107,18 +107,15 @@ mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
*/
static void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac)
{
- struct mwifiex_sta_node *node, *tmp;
+ struct mwifiex_sta_node *node;
unsigned long flags;
spin_lock_irqsave(&priv->sta_list_spinlock, flags);
node = mwifiex_get_sta_entry(priv, mac);
if (node) {
- list_for_each_entry_safe(node, tmp, &priv->sta_list,
- list) {
- list_del(&node->list);
- kfree(node);
- }
+ list_del(&node->list);
+ kfree(node);
}
spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
@@ -295,3 +292,19 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
return 0;
}
+
+/* This function deletes station entry from associated station list.
+ * Also if both AP and STA are 11n enabled, RxReorder tables and TxBA stream
+ * tables created for this station are deleted.
+ */
+void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
+ struct mwifiex_sta_node *node)
+{
+ if (priv->ap_11n_enabled && node->is_11n_enabled) {
+ mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, node->mac_addr);
+ mwifiex_del_tx_ba_stream_tbl_by_ra(priv, node->mac_addr);
+ }
+ mwifiex_del_sta_entry(priv, node->mac_addr);
+
+ return;
+}
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index 6820fce4016b..a3707fd4ef62 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -1548,7 +1548,7 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
if (!priv->pending_tx_pkts)
return 0;
- retry = 0;
+ retry = 1;
rc = 0;
spin_lock_bh(&priv->tx_lock);
@@ -1572,13 +1572,19 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
spin_lock_bh(&priv->tx_lock);
- if (timeout) {
+ if (timeout || !priv->pending_tx_pkts) {
WARN_ON(priv->pending_tx_pkts);
if (retry)
wiphy_notice(hw->wiphy, "tx rings drained\n");
break;
}
+ if (retry) {
+ mwl8k_tx_start(priv);
+ retry = 0;
+ continue;
+ }
+
if (priv->pending_tx_pkts < oldcount) {
wiphy_notice(hw->wiphy,
"waiting for tx rings to drain (%d -> %d pkts)\n",
@@ -2055,6 +2061,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw,
mwl8k_remove_stream(hw, stream);
spin_unlock(&priv->stream_lock);
}
+ mwl8k_tx_start(priv);
spin_unlock_bh(&priv->tx_lock);
pci_unmap_single(priv->pdev, dma, skb->len,
PCI_DMA_TODEVICE);
diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c
index 978e7eb26567..7fc46f26cf2b 100644
--- a/drivers/net/wireless/p54/p54spi.c
+++ b/drivers/net/wireless/p54/p54spi.c
@@ -42,8 +42,7 @@
MODULE_FIRMWARE("3826.arm");
-/*
- * gpios should be handled in board files and provided via platform data,
+/* gpios should be handled in board files and provided via platform data,
* but because it's currently impossible for p54spi to have a header file
* in include/linux, let's use module paramaters for now
*/
@@ -191,8 +190,7 @@ static int p54spi_request_eeprom(struct ieee80211_hw *dev)
const struct firmware *eeprom;
int ret;
- /*
- * allow users to customize their eeprom.
+ /* allow users to customize their eeprom.
*/
ret = request_firmware(&eeprom, "3826.eeprom", &priv->spi->dev);
@@ -285,8 +283,7 @@ static void p54spi_power_on(struct p54s_priv *priv)
gpio_set_value(p54spi_gpio_power, 1);
enable_irq(gpio_to_irq(p54spi_gpio_irq));
- /*
- * need to wait a while before device can be accessed, the length
+ /* need to wait a while before device can be accessed, the length
* is just a guess
*/
msleep(10);
@@ -365,7 +362,8 @@ static int p54spi_rx(struct p54s_priv *priv)
/* Firmware may insert up to 4 padding bytes after the lmac header,
* but it does not amend the size of SPI data transfer.
* Such packets has correct data size in header, thus referencing
- * past the end of allocated skb. Reserve extra 4 bytes for this case */
+ * past the end of allocated skb. Reserve extra 4 bytes for this case
+ */
skb = dev_alloc_skb(len + 4);
if (!skb) {
p54spi_sleep(priv);
@@ -383,7 +381,8 @@ static int p54spi_rx(struct p54s_priv *priv)
}
p54spi_sleep(priv);
/* Put additional bytes to compensate for the possible
- * alignment-caused truncation */
+ * alignment-caused truncation
+ */
skb_put(skb, 4);
if (p54_rx(priv->hw, skb) == 0)
@@ -713,27 +712,7 @@ static struct spi_driver p54spi_driver = {
.remove = p54spi_remove,
};
-static int __init p54spi_init(void)
-{
- int ret;
-
- ret = spi_register_driver(&p54spi_driver);
- if (ret < 0) {
- printk(KERN_ERR "failed to register SPI driver: %d", ret);
- goto out;
- }
-
-out:
- return ret;
-}
-
-static void __exit p54spi_exit(void)
-{
- spi_unregister_driver(&p54spi_driver);
-}
-
-module_init(p54spi_init);
-module_exit(p54spi_exit);
+module_spi_driver(p54spi_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Christian Lamparter <chunkeey@web.de>");
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index b52d70c75e1a..ead3a3e746a2 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -840,7 +840,7 @@ static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev,
unsigned int beacon_base)
{
int i;
- const int txwi_desc_size = rt2x00dev->ops->bcn->winfo_size;
+ const int txwi_desc_size = rt2x00dev->bcn->winfo_size;
/*
* For the Beacon base registers we only need to clear
@@ -3953,379 +3953,577 @@ static void rt2800_init_bbp_early(struct rt2x00_dev *rt2x00dev)
rt2800_bbp_write(rt2x00dev, 106, 0x35);
}
-static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev)
+static void rt2800_disable_unused_dac_adc(struct rt2x00_dev *rt2x00dev)
{
- int ant, div_mode;
u16 eeprom;
u8 value;
- rt2800_init_bbp_early(rt2x00dev);
+ rt2800_bbp_read(rt2x00dev, 138, &value);
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+ if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
+ value |= 0x20;
+ if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
+ value &= ~0x02;
+ rt2800_bbp_write(rt2x00dev, 138, value);
+}
- rt2800_bbp_read(rt2x00dev, 105, &value);
- rt2x00_set_field8(&value, BBP105_MLD,
- rt2x00dev->default_ant.rx_chain_num == 2);
- rt2800_bbp_write(rt2x00dev, 105, value);
+static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev)
+{
+ rt2800_bbp_write(rt2x00dev, 31, 0x08);
+
+ rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+ rt2800_bbp_write(rt2x00dev, 66, 0x38);
+
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
+ rt2800_bbp_write(rt2x00dev, 73, 0x10);
+
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+
+ rt2800_bbp_write(rt2x00dev, 78, 0x0e);
+ rt2800_bbp_write(rt2x00dev, 80, 0x08);
+
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+ rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+ rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
+ rt2800_bbp_write(rt2x00dev, 86, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+ rt2800_bbp_write(rt2x00dev, 92, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+
+ rt2800_bbp_write(rt2x00dev, 105, 0x01);
+
+ rt2800_bbp_write(rt2x00dev, 106, 0x35);
+}
+
+static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev)
+{
+ rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+ rt2800_bbp_write(rt2x00dev, 66, 0x38);
+
+ if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) {
+ rt2800_bbp_write(rt2x00dev, 69, 0x16);
+ rt2800_bbp_write(rt2x00dev, 73, 0x12);
+ } else {
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
+ rt2800_bbp_write(rt2x00dev, 73, 0x10);
+ }
+
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+
+ rt2800_bbp_write(rt2x00dev, 81, 0x37);
+
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+ rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+ if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D))
+ rt2800_bbp_write(rt2x00dev, 84, 0x19);
+ else
+ rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
+ rt2800_bbp_write(rt2x00dev, 86, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+ rt2800_bbp_write(rt2x00dev, 92, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 103, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 105, 0x05);
+
+ rt2800_bbp_write(rt2x00dev, 106, 0x35);
+}
+
+static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev)
+{
+ rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+ rt2800_bbp_write(rt2x00dev, 66, 0x38);
+
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
+ rt2800_bbp_write(rt2x00dev, 73, 0x10);
+
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+
+ rt2800_bbp_write(rt2x00dev, 79, 0x13);
+ rt2800_bbp_write(rt2x00dev, 80, 0x05);
+ rt2800_bbp_write(rt2x00dev, 81, 0x33);
+
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+ rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+ rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
+ rt2800_bbp_write(rt2x00dev, 86, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+ rt2800_bbp_write(rt2x00dev, 92, 0x00);
+
+ if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) ||
+ rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) ||
+ rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E))
+ rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+ else
+ rt2800_bbp_write(rt2x00dev, 103, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 105, 0x05);
+
+ rt2800_bbp_write(rt2x00dev, 106, 0x35);
+
+ if (rt2x00_rt(rt2x00dev, RT3071) ||
+ rt2x00_rt(rt2x00dev, RT3090))
+ rt2800_disable_unused_dac_adc(rt2x00dev);
+}
+
+static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev)
+{
+ u8 value;
rt2800_bbp4_mac_if_ctrl(rt2x00dev);
- rt2800_bbp_write(rt2x00dev, 20, 0x06);
rt2800_bbp_write(rt2x00dev, 31, 0x08);
- rt2800_bbp_write(rt2x00dev, 65, 0x2C);
- rt2800_bbp_write(rt2x00dev, 68, 0xDD);
- rt2800_bbp_write(rt2x00dev, 69, 0x1A);
- rt2800_bbp_write(rt2x00dev, 70, 0x05);
+
+ rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+ rt2800_bbp_write(rt2x00dev, 66, 0x38);
+
+ rt2800_bbp_write(rt2x00dev, 68, 0x0b);
+
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
rt2800_bbp_write(rt2x00dev, 73, 0x13);
- rt2800_bbp_write(rt2x00dev, 74, 0x0F);
- rt2800_bbp_write(rt2x00dev, 75, 0x4F);
+ rt2800_bbp_write(rt2x00dev, 75, 0x46);
rt2800_bbp_write(rt2x00dev, 76, 0x28);
+
+ rt2800_bbp_write(rt2x00dev, 77, 0x58);
+
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+
+ rt2800_bbp_write(rt2x00dev, 74, 0x0b);
+ rt2800_bbp_write(rt2x00dev, 79, 0x18);
+ rt2800_bbp_write(rt2x00dev, 80, 0x09);
+ rt2800_bbp_write(rt2x00dev, 81, 0x33);
+
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+ rt2800_bbp_write(rt2x00dev, 83, 0x7a);
+
+ rt2800_bbp_write(rt2x00dev, 84, 0x9a);
+
+ rt2800_bbp_write(rt2x00dev, 86, 0x38);
+
+ rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+ rt2800_bbp_write(rt2x00dev, 92, 0x02);
+
+ rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+
+ rt2800_bbp_write(rt2x00dev, 104, 0x92);
+
+ rt2800_bbp_write(rt2x00dev, 105, 0x1c);
+
+ rt2800_bbp_write(rt2x00dev, 106, 0x03);
+
+ rt2800_bbp_write(rt2x00dev, 128, 0x12);
+
+ rt2800_bbp_write(rt2x00dev, 67, 0x24);
+ rt2800_bbp_write(rt2x00dev, 143, 0x04);
+ rt2800_bbp_write(rt2x00dev, 142, 0x99);
+ rt2800_bbp_write(rt2x00dev, 150, 0x30);
+ rt2800_bbp_write(rt2x00dev, 151, 0x2e);
+ rt2800_bbp_write(rt2x00dev, 152, 0x20);
+ rt2800_bbp_write(rt2x00dev, 153, 0x34);
+ rt2800_bbp_write(rt2x00dev, 154, 0x40);
+ rt2800_bbp_write(rt2x00dev, 155, 0x3b);
+ rt2800_bbp_write(rt2x00dev, 253, 0x04);
+
+ rt2800_bbp_read(rt2x00dev, 47, &value);
+ rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1);
+ rt2800_bbp_write(rt2x00dev, 47, value);
+
+ /* Use 5-bit ADC for Acquisition and 8-bit ADC for data */
+ rt2800_bbp_read(rt2x00dev, 3, &value);
+ rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1);
+ rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1);
+ rt2800_bbp_write(rt2x00dev, 3, value);
+}
+
+static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev)
+{
+ rt2800_bbp_write(rt2x00dev, 3, 0x00);
+ rt2800_bbp_write(rt2x00dev, 4, 0x50);
+
+ rt2800_bbp_write(rt2x00dev, 31, 0x08);
+
+ rt2800_bbp_write(rt2x00dev, 47, 0x48);
+
+ rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+ rt2800_bbp_write(rt2x00dev, 66, 0x38);
+
+ rt2800_bbp_write(rt2x00dev, 68, 0x0b);
+
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
+ rt2800_bbp_write(rt2x00dev, 73, 0x13);
+ rt2800_bbp_write(rt2x00dev, 75, 0x46);
+ rt2800_bbp_write(rt2x00dev, 76, 0x28);
+
rt2800_bbp_write(rt2x00dev, 77, 0x59);
- rt2800_bbp_write(rt2x00dev, 84, 0x9A);
+
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+
+ rt2800_bbp_write(rt2x00dev, 78, 0x0e);
+ rt2800_bbp_write(rt2x00dev, 80, 0x08);
+ rt2800_bbp_write(rt2x00dev, 81, 0x37);
+
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+ rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+ rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
rt2800_bbp_write(rt2x00dev, 86, 0x38);
+
rt2800_bbp_write(rt2x00dev, 88, 0x90);
+
rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
rt2800_bbp_write(rt2x00dev, 92, 0x02);
- rt2800_bbp_write(rt2x00dev, 95, 0x9a);
- rt2800_bbp_write(rt2x00dev, 98, 0x12);
- rt2800_bbp_write(rt2x00dev, 103, 0xC0);
+
+ rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+
rt2800_bbp_write(rt2x00dev, 104, 0x92);
- /* FIXME BBP105 owerwrite */
- rt2800_bbp_write(rt2x00dev, 105, 0x3C);
- rt2800_bbp_write(rt2x00dev, 106, 0x35);
- rt2800_bbp_write(rt2x00dev, 128, 0x12);
- rt2800_bbp_write(rt2x00dev, 134, 0xD0);
- rt2800_bbp_write(rt2x00dev, 135, 0xF6);
- rt2800_bbp_write(rt2x00dev, 137, 0x0F);
- /* Initialize GLRT (Generalized Likehood Radio Test) */
- rt2800_init_bbp_5592_glrt(rt2x00dev);
+ rt2800_bbp_write(rt2x00dev, 105, 0x34);
+
+ rt2800_bbp_write(rt2x00dev, 106, 0x05);
+
+ rt2800_bbp_write(rt2x00dev, 120, 0x50);
+
+ rt2800_bbp_write(rt2x00dev, 137, 0x0f);
+
+ rt2800_bbp_write(rt2x00dev, 163, 0xbd);
+ /* Set ITxBF timeout to 0x9c40=1000msec */
+ rt2800_bbp_write(rt2x00dev, 179, 0x02);
+ rt2800_bbp_write(rt2x00dev, 180, 0x00);
+ rt2800_bbp_write(rt2x00dev, 182, 0x40);
+ rt2800_bbp_write(rt2x00dev, 180, 0x01);
+ rt2800_bbp_write(rt2x00dev, 182, 0x9c);
+ rt2800_bbp_write(rt2x00dev, 179, 0x00);
+ /* Reprogram the inband interface to put right values in RXWI */
+ rt2800_bbp_write(rt2x00dev, 142, 0x04);
+ rt2800_bbp_write(rt2x00dev, 143, 0x3b);
+ rt2800_bbp_write(rt2x00dev, 142, 0x06);
+ rt2800_bbp_write(rt2x00dev, 143, 0xa0);
+ rt2800_bbp_write(rt2x00dev, 142, 0x07);
+ rt2800_bbp_write(rt2x00dev, 143, 0xa1);
+ rt2800_bbp_write(rt2x00dev, 142, 0x08);
+ rt2800_bbp_write(rt2x00dev, 143, 0xa2);
+
+ rt2800_bbp_write(rt2x00dev, 148, 0xc8);
+}
- rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev)
+{
+ rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+ rt2800_bbp_write(rt2x00dev, 66, 0x38);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
- div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY);
- ant = (div_mode == 3) ? 1 : 0;
- rt2800_bbp_read(rt2x00dev, 152, &value);
- if (ant == 0) {
- /* Main antenna */
- rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1);
- } else {
- /* Auxiliary antenna */
- rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0);
- }
- rt2800_bbp_write(rt2x00dev, 152, value);
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
+ rt2800_bbp_write(rt2x00dev, 73, 0x10);
- if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) {
- rt2800_bbp_read(rt2x00dev, 254, &value);
- rt2x00_set_field8(&value, BBP254_BIT7, 1);
- rt2800_bbp_write(rt2x00dev, 254, value);
- }
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
- rt2800_init_freq_calibration(rt2x00dev);
+ rt2800_bbp_write(rt2x00dev, 79, 0x13);
+ rt2800_bbp_write(rt2x00dev, 80, 0x05);
+ rt2800_bbp_write(rt2x00dev, 81, 0x33);
- rt2800_bbp_write(rt2x00dev, 84, 0x19);
- if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C))
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+ rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+ rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
+ rt2800_bbp_write(rt2x00dev, 86, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+ rt2800_bbp_write(rt2x00dev, 92, 0x00);
+
+ if (rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E))
rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+ else
+ rt2800_bbp_write(rt2x00dev, 103, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 105, 0x05);
+
+ rt2800_bbp_write(rt2x00dev, 106, 0x35);
+
+ rt2800_disable_unused_dac_adc(rt2x00dev);
}
-static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
+static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev)
{
- unsigned int i;
- u16 eeprom;
- u8 reg_id;
- u8 value;
+ rt2800_bbp_write(rt2x00dev, 31, 0x08);
- if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) ||
- rt2800_wait_bbp_ready(rt2x00dev)))
- return -EACCES;
+ rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+ rt2800_bbp_write(rt2x00dev, 66, 0x38);
- if (rt2x00_rt(rt2x00dev, RT5592)) {
- rt2800_init_bbp_5592(rt2x00dev);
- return 0;
- }
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
+ rt2800_bbp_write(rt2x00dev, 73, 0x10);
- if (rt2x00_rt(rt2x00dev, RT3352)) {
- rt2800_bbp_write(rt2x00dev, 3, 0x00);
- rt2800_bbp_write(rt2x00dev, 4, 0x50);
- }
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
- if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+ rt2800_bbp_write(rt2x00dev, 79, 0x13);
+ rt2800_bbp_write(rt2x00dev, 80, 0x05);
+ rt2800_bbp_write(rt2x00dev, 81, 0x33);
- if (rt2800_is_305x_soc(rt2x00dev) ||
- rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT3352) ||
- rt2x00_rt(rt2x00dev, RT3572) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 31, 0x08);
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
- if (rt2x00_rt(rt2x00dev, RT3352))
- rt2800_bbp_write(rt2x00dev, 47, 0x48);
+ rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+ rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
+ rt2800_bbp_write(rt2x00dev, 86, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+ rt2800_bbp_write(rt2x00dev, 92, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+
+ rt2800_bbp_write(rt2x00dev, 105, 0x05);
+
+ rt2800_bbp_write(rt2x00dev, 106, 0x35);
+
+ rt2800_disable_unused_dac_adc(rt2x00dev);
+}
+
+static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev)
+{
+ int ant, div_mode;
+ u16 eeprom;
+ u8 value;
+
+ rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+
+ rt2800_bbp_write(rt2x00dev, 31, 0x08);
rt2800_bbp_write(rt2x00dev, 65, 0x2c);
rt2800_bbp_write(rt2x00dev, 66, 0x38);
- if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT3352) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 68, 0x0b);
+ rt2800_bbp_write(rt2x00dev, 68, 0x0b);
- if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) {
- rt2800_bbp_write(rt2x00dev, 69, 0x16);
- rt2800_bbp_write(rt2x00dev, 73, 0x12);
- } else if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT3352) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392)) {
- rt2800_bbp_write(rt2x00dev, 69, 0x12);
- rt2800_bbp_write(rt2x00dev, 73, 0x13);
- rt2800_bbp_write(rt2x00dev, 75, 0x46);
- rt2800_bbp_write(rt2x00dev, 76, 0x28);
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
+ rt2800_bbp_write(rt2x00dev, 73, 0x13);
+ rt2800_bbp_write(rt2x00dev, 75, 0x46);
+ rt2800_bbp_write(rt2x00dev, 76, 0x28);
- if (rt2x00_rt(rt2x00dev, RT3290))
- rt2800_bbp_write(rt2x00dev, 77, 0x58);
- else
- rt2800_bbp_write(rt2x00dev, 77, 0x59);
- } else {
- rt2800_bbp_write(rt2x00dev, 69, 0x12);
- rt2800_bbp_write(rt2x00dev, 73, 0x10);
- }
+ rt2800_bbp_write(rt2x00dev, 77, 0x59);
rt2800_bbp_write(rt2x00dev, 70, 0x0a);
- if (rt2x00_rt(rt2x00dev, RT3070) ||
- rt2x00_rt(rt2x00dev, RT3071) ||
- rt2x00_rt(rt2x00dev, RT3090) ||
- rt2x00_rt(rt2x00dev, RT3390) ||
- rt2x00_rt(rt2x00dev, RT3572) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392)) {
- rt2800_bbp_write(rt2x00dev, 79, 0x13);
- rt2800_bbp_write(rt2x00dev, 80, 0x05);
- rt2800_bbp_write(rt2x00dev, 81, 0x33);
- } else if (rt2800_is_305x_soc(rt2x00dev)) {
- rt2800_bbp_write(rt2x00dev, 78, 0x0e);
- rt2800_bbp_write(rt2x00dev, 80, 0x08);
- } else if (rt2x00_rt(rt2x00dev, RT3290)) {
- rt2800_bbp_write(rt2x00dev, 74, 0x0b);
- rt2800_bbp_write(rt2x00dev, 79, 0x18);
- rt2800_bbp_write(rt2x00dev, 80, 0x09);
- rt2800_bbp_write(rt2x00dev, 81, 0x33);
- } else if (rt2x00_rt(rt2x00dev, RT3352)) {
- rt2800_bbp_write(rt2x00dev, 78, 0x0e);
- rt2800_bbp_write(rt2x00dev, 80, 0x08);
- rt2800_bbp_write(rt2x00dev, 81, 0x37);
- } else {
- rt2800_bbp_write(rt2x00dev, 81, 0x37);
- }
+ rt2800_bbp_write(rt2x00dev, 79, 0x13);
+ rt2800_bbp_write(rt2x00dev, 80, 0x05);
+ rt2800_bbp_write(rt2x00dev, 81, 0x33);
rt2800_bbp_write(rt2x00dev, 82, 0x62);
- if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 83, 0x7a);
- else
- rt2800_bbp_write(rt2x00dev, 83, 0x6a);
- if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D))
- rt2800_bbp_write(rt2x00dev, 84, 0x19);
- else if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 84, 0x9a);
- else
- rt2800_bbp_write(rt2x00dev, 84, 0x99);
+ rt2800_bbp_write(rt2x00dev, 83, 0x7a);
- if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT3352) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 86, 0x38);
- else
- rt2800_bbp_write(rt2x00dev, 86, 0x00);
+ rt2800_bbp_write(rt2x00dev, 84, 0x9a);
- if (rt2x00_rt(rt2x00dev, RT3352) ||
- rt2x00_rt(rt2x00dev, RT5392))
+ rt2800_bbp_write(rt2x00dev, 86, 0x38);
+
+ if (rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 88, 0x90);
rt2800_bbp_write(rt2x00dev, 91, 0x04);
- if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT3352) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 92, 0x02);
- else
- rt2800_bbp_write(rt2x00dev, 92, 0x00);
+ rt2800_bbp_write(rt2x00dev, 92, 0x02);
if (rt2x00_rt(rt2x00dev, RT5392)) {
rt2800_bbp_write(rt2x00dev, 95, 0x9a);
rt2800_bbp_write(rt2x00dev, 98, 0x12);
}
- if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) ||
- rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) ||
- rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E) ||
- rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E) ||
- rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT3352) ||
- rt2x00_rt(rt2x00dev, RT3572) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392) ||
- rt2800_is_305x_soc(rt2x00dev))
- rt2800_bbp_write(rt2x00dev, 103, 0xc0);
- else
- rt2800_bbp_write(rt2x00dev, 103, 0x00);
+ rt2800_bbp_write(rt2x00dev, 103, 0xc0);
- if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT3352) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 104, 0x92);
+ rt2800_bbp_write(rt2x00dev, 104, 0x92);
- if (rt2800_is_305x_soc(rt2x00dev))
- rt2800_bbp_write(rt2x00dev, 105, 0x01);
- else if (rt2x00_rt(rt2x00dev, RT3290))
- rt2800_bbp_write(rt2x00dev, 105, 0x1c);
- else if (rt2x00_rt(rt2x00dev, RT3352))
- rt2800_bbp_write(rt2x00dev, 105, 0x34);
- else if (rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 105, 0x3c);
- else
- rt2800_bbp_write(rt2x00dev, 105, 0x05);
+ rt2800_bbp_write(rt2x00dev, 105, 0x3c);
- if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT5390))
+ if (rt2x00_rt(rt2x00dev, RT5390))
rt2800_bbp_write(rt2x00dev, 106, 0x03);
- else if (rt2x00_rt(rt2x00dev, RT3352))
- rt2800_bbp_write(rt2x00dev, 106, 0x05);
else if (rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 106, 0x12);
else
- rt2800_bbp_write(rt2x00dev, 106, 0x35);
-
- if (rt2x00_rt(rt2x00dev, RT3352))
- rt2800_bbp_write(rt2x00dev, 120, 0x50);
+ WARN_ON(1);
- if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 128, 0x12);
+ rt2800_bbp_write(rt2x00dev, 128, 0x12);
if (rt2x00_rt(rt2x00dev, RT5392)) {
rt2800_bbp_write(rt2x00dev, 134, 0xd0);
rt2800_bbp_write(rt2x00dev, 135, 0xf6);
}
- if (rt2x00_rt(rt2x00dev, RT3352))
- rt2800_bbp_write(rt2x00dev, 137, 0x0f);
+ rt2800_disable_unused_dac_adc(rt2x00dev);
- if (rt2x00_rt(rt2x00dev, RT3071) ||
- rt2x00_rt(rt2x00dev, RT3090) ||
- rt2x00_rt(rt2x00dev, RT3390) ||
- rt2x00_rt(rt2x00dev, RT3572) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392)) {
- rt2800_bbp_read(rt2x00dev, 138, &value);
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+ div_mode = rt2x00_get_field16(eeprom,
+ EEPROM_NIC_CONF1_ANT_DIVERSITY);
+ ant = (div_mode == 3) ? 1 : 0;
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
- if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
- value |= 0x20;
- if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
- value &= ~0x02;
+ /* check if this is a Bluetooth combo card */
+ if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
+ u32 reg;
- rt2800_bbp_write(rt2x00dev, 138, value);
+ rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
+ rt2x00_set_field32(&reg, GPIO_CTRL_DIR3, 0);
+ rt2x00_set_field32(&reg, GPIO_CTRL_DIR6, 0);
+ rt2x00_set_field32(&reg, GPIO_CTRL_VAL3, 0);
+ rt2x00_set_field32(&reg, GPIO_CTRL_VAL6, 0);
+ if (ant == 0)
+ rt2x00_set_field32(&reg, GPIO_CTRL_VAL3, 1);
+ else if (ant == 1)
+ rt2x00_set_field32(&reg, GPIO_CTRL_VAL6, 1);
+ rt2800_register_write(rt2x00dev, GPIO_CTRL, reg);
}
- if (rt2x00_rt(rt2x00dev, RT3290)) {
- rt2800_bbp_write(rt2x00dev, 67, 0x24);
- rt2800_bbp_write(rt2x00dev, 143, 0x04);
- rt2800_bbp_write(rt2x00dev, 142, 0x99);
- rt2800_bbp_write(rt2x00dev, 150, 0x30);
- rt2800_bbp_write(rt2x00dev, 151, 0x2e);
- rt2800_bbp_write(rt2x00dev, 152, 0x20);
- rt2800_bbp_write(rt2x00dev, 153, 0x34);
- rt2800_bbp_write(rt2x00dev, 154, 0x40);
- rt2800_bbp_write(rt2x00dev, 155, 0x3b);
- rt2800_bbp_write(rt2x00dev, 253, 0x04);
-
- rt2800_bbp_read(rt2x00dev, 47, &value);
- rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1);
- rt2800_bbp_write(rt2x00dev, 47, value);
-
- /* Use 5-bit ADC for Acquisition and 8-bit ADC for data */
- rt2800_bbp_read(rt2x00dev, 3, &value);
- rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1);
- rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1);
- rt2800_bbp_write(rt2x00dev, 3, value);
+ /* This chip has hardware antenna diversity*/
+ if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) {
+ rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */
+ rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */
+ rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */
}
- if (rt2x00_rt(rt2x00dev, RT3352)) {
- rt2800_bbp_write(rt2x00dev, 163, 0xbd);
- /* Set ITxBF timeout to 0x9c40=1000msec */
- rt2800_bbp_write(rt2x00dev, 179, 0x02);
- rt2800_bbp_write(rt2x00dev, 180, 0x00);
- rt2800_bbp_write(rt2x00dev, 182, 0x40);
- rt2800_bbp_write(rt2x00dev, 180, 0x01);
- rt2800_bbp_write(rt2x00dev, 182, 0x9c);
- rt2800_bbp_write(rt2x00dev, 179, 0x00);
- /* Reprogram the inband interface to put right values in RXWI */
- rt2800_bbp_write(rt2x00dev, 142, 0x04);
- rt2800_bbp_write(rt2x00dev, 143, 0x3b);
- rt2800_bbp_write(rt2x00dev, 142, 0x06);
- rt2800_bbp_write(rt2x00dev, 143, 0xa0);
- rt2800_bbp_write(rt2x00dev, 142, 0x07);
- rt2800_bbp_write(rt2x00dev, 143, 0xa1);
- rt2800_bbp_write(rt2x00dev, 142, 0x08);
- rt2800_bbp_write(rt2x00dev, 143, 0xa2);
-
- rt2800_bbp_write(rt2x00dev, 148, 0xc8);
+ rt2800_bbp_read(rt2x00dev, 152, &value);
+ if (ant == 0)
+ rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1);
+ else
+ rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0);
+ rt2800_bbp_write(rt2x00dev, 152, value);
+
+ rt2800_init_freq_calibration(rt2x00dev);
+}
+
+static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev)
+{
+ int ant, div_mode;
+ u16 eeprom;
+ u8 value;
+
+ rt2800_init_bbp_early(rt2x00dev);
+
+ rt2800_bbp_read(rt2x00dev, 105, &value);
+ rt2x00_set_field8(&value, BBP105_MLD,
+ rt2x00dev->default_ant.rx_chain_num == 2);
+ rt2800_bbp_write(rt2x00dev, 105, value);
+
+ rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+
+ rt2800_bbp_write(rt2x00dev, 20, 0x06);
+ rt2800_bbp_write(rt2x00dev, 31, 0x08);
+ rt2800_bbp_write(rt2x00dev, 65, 0x2C);
+ rt2800_bbp_write(rt2x00dev, 68, 0xDD);
+ rt2800_bbp_write(rt2x00dev, 69, 0x1A);
+ rt2800_bbp_write(rt2x00dev, 70, 0x05);
+ rt2800_bbp_write(rt2x00dev, 73, 0x13);
+ rt2800_bbp_write(rt2x00dev, 74, 0x0F);
+ rt2800_bbp_write(rt2x00dev, 75, 0x4F);
+ rt2800_bbp_write(rt2x00dev, 76, 0x28);
+ rt2800_bbp_write(rt2x00dev, 77, 0x59);
+ rt2800_bbp_write(rt2x00dev, 84, 0x9A);
+ rt2800_bbp_write(rt2x00dev, 86, 0x38);
+ rt2800_bbp_write(rt2x00dev, 88, 0x90);
+ rt2800_bbp_write(rt2x00dev, 91, 0x04);
+ rt2800_bbp_write(rt2x00dev, 92, 0x02);
+ rt2800_bbp_write(rt2x00dev, 95, 0x9a);
+ rt2800_bbp_write(rt2x00dev, 98, 0x12);
+ rt2800_bbp_write(rt2x00dev, 103, 0xC0);
+ rt2800_bbp_write(rt2x00dev, 104, 0x92);
+ /* FIXME BBP105 owerwrite */
+ rt2800_bbp_write(rt2x00dev, 105, 0x3C);
+ rt2800_bbp_write(rt2x00dev, 106, 0x35);
+ rt2800_bbp_write(rt2x00dev, 128, 0x12);
+ rt2800_bbp_write(rt2x00dev, 134, 0xD0);
+ rt2800_bbp_write(rt2x00dev, 135, 0xF6);
+ rt2800_bbp_write(rt2x00dev, 137, 0x0F);
+
+ /* Initialize GLRT (Generalized Likehood Radio Test) */
+ rt2800_init_bbp_5592_glrt(rt2x00dev);
+
+ rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+ div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY);
+ ant = (div_mode == 3) ? 1 : 0;
+ rt2800_bbp_read(rt2x00dev, 152, &value);
+ if (ant == 0) {
+ /* Main antenna */
+ rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1);
+ } else {
+ /* Auxiliary antenna */
+ rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0);
}
+ rt2800_bbp_write(rt2x00dev, 152, value);
- if (rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392)) {
- int ant, div_mode;
+ if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) {
+ rt2800_bbp_read(rt2x00dev, 254, &value);
+ rt2x00_set_field8(&value, BBP254_BIT7, 1);
+ rt2800_bbp_write(rt2x00dev, 254, value);
+ }
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
- div_mode = rt2x00_get_field16(eeprom,
- EEPROM_NIC_CONF1_ANT_DIVERSITY);
- ant = (div_mode == 3) ? 1 : 0;
+ rt2800_init_freq_calibration(rt2x00dev);
- /* check if this is a Bluetooth combo card */
- if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
- u32 reg;
-
- rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
- rt2x00_set_field32(&reg, GPIO_CTRL_DIR3, 0);
- rt2x00_set_field32(&reg, GPIO_CTRL_DIR6, 0);
- rt2x00_set_field32(&reg, GPIO_CTRL_VAL3, 0);
- rt2x00_set_field32(&reg, GPIO_CTRL_VAL6, 0);
- if (ant == 0)
- rt2x00_set_field32(&reg, GPIO_CTRL_VAL3, 1);
- else if (ant == 1)
- rt2x00_set_field32(&reg, GPIO_CTRL_VAL6, 1);
- rt2800_register_write(rt2x00dev, GPIO_CTRL, reg);
- }
+ rt2800_bbp_write(rt2x00dev, 84, 0x19);
+ if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C))
+ rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+}
- /* This chip has hardware antenna diversity*/
- if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) {
- rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */
- rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */
- rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */
- }
+static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
+{
+ unsigned int i;
+ u16 eeprom;
+ u8 reg_id;
+ u8 value;
- rt2800_bbp_read(rt2x00dev, 152, &value);
- if (ant == 0)
- rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1);
- else
- rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0);
- rt2800_bbp_write(rt2x00dev, 152, value);
+ if (rt2800_is_305x_soc(rt2x00dev))
+ rt2800_init_bbp_305x_soc(rt2x00dev);
- rt2800_init_freq_calibration(rt2x00dev);
+ switch (rt2x00dev->chip.rt) {
+ case RT2860:
+ case RT2872:
+ case RT2883:
+ rt2800_init_bbp_28xx(rt2x00dev);
+ break;
+ case RT3070:
+ case RT3071:
+ case RT3090:
+ rt2800_init_bbp_30xx(rt2x00dev);
+ break;
+ case RT3290:
+ rt2800_init_bbp_3290(rt2x00dev);
+ break;
+ case RT3352:
+ rt2800_init_bbp_3352(rt2x00dev);
+ break;
+ case RT3390:
+ rt2800_init_bbp_3390(rt2x00dev);
+ break;
+ case RT3572:
+ rt2800_init_bbp_3572(rt2x00dev);
+ break;
+ case RT5390:
+ case RT5392:
+ rt2800_init_bbp_53xx(rt2x00dev);
+ break;
+ case RT5592:
+ rt2800_init_bbp_5592(rt2x00dev);
+ return;
}
for (i = 0; i < EEPROM_BBP_SIZE; i++) {
@@ -4337,8 +4535,6 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
rt2800_bbp_write(rt2x00dev, reg_id, value);
}
}
-
- return 0;
}
static void rt2800_led_open_drain_enable(struct rt2x00_dev *rt2x00dev)
@@ -5189,9 +5385,11 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev)
}
msleep(1);
- if (unlikely(rt2800_init_bbp(rt2x00dev)))
+ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) ||
+ rt2800_wait_bbp_ready(rt2x00dev)))
return -EIO;
+ rt2800_init_bbp(rt2x00dev);
rt2800_init_rfcsr(rt2x00dev);
if (rt2x00_is_usb(rt2x00dev) &&
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index 6f4a861af336..330f1d25726d 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -1014,7 +1014,7 @@ static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
* Since we have only one producer and one consumer we don't
* need to lock the kfifo.
*/
- for (i = 0; i < rt2x00dev->ops->tx->entry_num; i++) {
+ for (i = 0; i < rt2x00dev->tx->limit; i++) {
rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status);
if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID))
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index ac854d75bd6c..c71a48da9a31 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -327,7 +327,7 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev)
* this limit so reduce the number to prevent errors.
*/
rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_AGG_LIMIT,
- ((rt2x00dev->ops->rx->entry_num * DATA_FRAME_SIZE)
+ ((rt2x00dev->rx->limit * DATA_FRAME_SIZE)
/ 1024) - 3);
rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_EN, 1);
rt2x00_set_field32(&reg, USB_DMA_CFG_TX_BULK_EN, 1);
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 90dc14336980..6a201725bc50 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -1077,7 +1077,7 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev)
*/
int kfifo_size =
roundup_pow_of_two(rt2x00dev->ops->tx_queues *
- rt2x00dev->ops->tx->entry_num *
+ rt2x00dev->tx->limit *
sizeof(u32));
status = kfifo_alloc(&rt2x00dev->txstatus_fifo, kfifo_size,
@@ -1301,23 +1301,6 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
(rt2x00dev->ops->max_ap_intf - 1);
/*
- * Determine which operating modes are supported, all modes
- * which require beaconing, depend on the availability of
- * beacon entries.
- */
- rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
- if (rt2x00dev->ops->bcn->entry_num > 0)
- rt2x00dev->hw->wiphy->interface_modes |=
- BIT(NL80211_IFTYPE_ADHOC) |
- BIT(NL80211_IFTYPE_AP) |
-#ifdef CONFIG_MAC80211_MESH
- BIT(NL80211_IFTYPE_MESH_POINT) |
-#endif
- BIT(NL80211_IFTYPE_WDS);
-
- rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
-
- /*
* Initialize work.
*/
rt2x00dev->workqueue =
@@ -1348,6 +1331,23 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
goto exit;
/*
+ * Determine which operating modes are supported, all modes
+ * which require beaconing, depend on the availability of
+ * beacon entries.
+ */
+ rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+ if (rt2x00dev->ops->bcn->entry_num > 0)
+ rt2x00dev->hw->wiphy->interface_modes |=
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP) |
+#ifdef CONFIG_MAC80211_MESH
+ BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+ BIT(NL80211_IFTYPE_WDS);
+
+ rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+
+ /*
* Initialize ieee80211 structure.
*/
retval = rt2x00lib_probe_hw(rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c
index dc49e525ae5e..76d95deb274b 100644
--- a/drivers/net/wireless/rt2x00/rt2x00pci.c
+++ b/drivers/net/wireless/rt2x00/rt2x00pci.c
@@ -105,11 +105,13 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops)
goto exit_release_regions;
}
+ pci_enable_msi(pci_dev);
+
hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw);
if (!hw) {
rt2x00_probe_err("Failed to allocate hardware\n");
retval = -ENOMEM;
- goto exit_release_regions;
+ goto exit_disable_msi;
}
pci_set_drvdata(pci_dev, hw);
@@ -150,6 +152,9 @@ exit_free_reg:
exit_free_device:
ieee80211_free_hw(hw);
+exit_disable_msi:
+ pci_disable_msi(pci_dev);
+
exit_release_regions:
pci_release_regions(pci_dev);
@@ -174,6 +179,8 @@ void rt2x00pci_remove(struct pci_dev *pci_dev)
rt2x00pci_free_reg(rt2x00dev);
ieee80211_free_hw(hw);
+ pci_disable_msi(pci_dev);
+
/*
* Free the PCI device data.
*/
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 2c12311467a9..5efbbbdca701 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -1170,12 +1170,6 @@ static int rt2x00queue_alloc_entries(struct data_queue *queue,
rt2x00queue_reset(queue);
- queue->limit = qdesc->entry_num;
- queue->threshold = DIV_ROUND_UP(qdesc->entry_num, 10);
- queue->data_size = qdesc->data_size;
- queue->desc_size = qdesc->desc_size;
- queue->winfo_size = qdesc->winfo_size;
-
/*
* Allocate all queue entries.
*/
@@ -1284,9 +1278,38 @@ void rt2x00queue_uninitialize(struct rt2x00_dev *rt2x00dev)
}
}
+static const struct data_queue_desc *
+rt2x00queue_get_qdesc_by_qid(struct rt2x00_dev *rt2x00dev,
+ enum data_queue_qid qid)
+{
+ switch (qid) {
+ case QID_RX:
+ return rt2x00dev->ops->rx;
+
+ case QID_AC_BE:
+ case QID_AC_BK:
+ case QID_AC_VO:
+ case QID_AC_VI:
+ return rt2x00dev->ops->tx;
+
+ case QID_BEACON:
+ return rt2x00dev->ops->bcn;
+
+ case QID_ATIM:
+ return rt2x00dev->ops->atim;
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev,
struct data_queue *queue, enum data_queue_qid qid)
{
+ const struct data_queue_desc *qdesc;
+
mutex_init(&queue->status_lock);
spin_lock_init(&queue->tx_lock);
spin_lock_init(&queue->index_lock);
@@ -1297,6 +1320,15 @@ static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev,
queue->aifs = 2;
queue->cw_min = 5;
queue->cw_max = 10;
+
+ qdesc = rt2x00queue_get_qdesc_by_qid(rt2x00dev, qid);
+ BUG_ON(!qdesc);
+
+ queue->limit = qdesc->entry_num;
+ queue->threshold = DIV_ROUND_UP(qdesc->entry_num, 10);
+ queue->data_size = qdesc->data_size;
+ queue->desc_size = qdesc->desc_size;
+ queue->winfo_size = qdesc->winfo_size;
}
int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev)
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index 0dc8180e251b..7e1759b3e49a 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -2175,7 +2175,7 @@ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev)
* that the TX_STA_FIFO stack has a size of 16. We stick to our
* tx ring size for now.
*/
- for (i = 0; i < rt2x00dev->ops->tx->entry_num; i++) {
+ for (i = 0; i < rt2x00dev->tx->limit; i++) {
rt2x00mmio_register_read(rt2x00dev, STA_CSR4, &reg);
if (!rt2x00_get_field32(reg, STA_CSR4_VALID))
break;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c
index 19a765532603..47875ba09ff8 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c
@@ -842,7 +842,7 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter(
long val_y, ele_c = 0;
u8 ofdm_index[2];
s8 cck_index = 0;
- u8 ofdm_index_old[2];
+ u8 ofdm_index_old[2] = {0, 0};
s8 cck_index_old = 0;
u8 index;
int i;
diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c
index a3a851e49321..18c0d8d1ddf7 100644
--- a/drivers/pcmcia/m8xx_pcmcia.c
+++ b/drivers/pcmcia/m8xx_pcmcia.c
@@ -68,12 +68,6 @@ MODULE_LICENSE("Dual MPL/GPL");
#if !defined(CONFIG_PCMCIA_SLOT_A) && !defined(CONFIG_PCMCIA_SLOT_B)
-/* The RPX series use SLOT_B */
-#if defined(CONFIG_RPXCLASSIC) || defined(CONFIG_RPXLITE)
-#define CONFIG_PCMCIA_SLOT_B
-#define CONFIG_BD_IS_MHZ
-#endif
-
/* The ADS board use SLOT_A */
#ifdef CONFIG_ADS
#define CONFIG_PCMCIA_SLOT_A
@@ -253,81 +247,6 @@ static irqreturn_t m8xx_interrupt(int irq, void *dev);
#define PCMCIA_BMT_LIMIT (15*4) /* Bus Monitor Timeout value */
-/* ------------------------------------------------------------------------- */
-/* board specific stuff: */
-/* voltage_set(), hardware_enable() and hardware_disable() */
-/* ------------------------------------------------------------------------- */
-/* RPX Boards from Embedded Planet */
-
-#if defined(CONFIG_RPXCLASSIC) || defined(CONFIG_RPXLITE)
-
-/* The RPX boards seems to have it's bus monitor timeout set to 6*8 clocks.
- * SYPCR is write once only, therefore must the slowest memory be faster
- * than the bus monitor or we will get a machine check due to the bus timeout.
- */
-
-#define PCMCIA_BOARD_MSG "RPX CLASSIC or RPX LITE"
-
-#undef PCMCIA_BMT_LIMIT
-#define PCMCIA_BMT_LIMIT (6*8)
-
-static int voltage_set(int slot, int vcc, int vpp)
-{
- u32 reg = 0;
-
- switch (vcc) {
- case 0:
- break;
- case 33:
- reg |= BCSR1_PCVCTL4;
- break;
- case 50:
- reg |= BCSR1_PCVCTL5;
- break;
- default:
- return 1;
- }
-
- switch (vpp) {
- case 0:
- break;
- case 33:
- case 50:
- if (vcc == vpp)
- reg |= BCSR1_PCVCTL6;
- else
- return 1;
- break;
- case 120:
- reg |= BCSR1_PCVCTL7;
- default:
- return 1;
- }
-
- if (!((vcc == 50) || (vcc == 0)))
- return 1;
-
- /* first, turn off all power */
-
- out_be32(((u32 *) RPX_CSR_ADDR),
- in_be32(((u32 *) RPX_CSR_ADDR)) & ~(BCSR1_PCVCTL4 |
- BCSR1_PCVCTL5 |
- BCSR1_PCVCTL6 |
- BCSR1_PCVCTL7));
-
- /* enable new powersettings */
-
- out_be32(((u32 *) RPX_CSR_ADDR), in_be32(((u32 *) RPX_CSR_ADDR)) | reg);
-
- return 0;
-}
-
-#define socket_get(_slot_) PCMCIA_SOCKET_KEY_5V
-#define hardware_enable(_slot_) /* No hardware to enable */
-#define hardware_disable(_slot_) /* No hardware to disable */
-
-#endif /* CONFIG_RPXCLASSIC */
-
/* FADS Boards from Motorola */
#if defined(CONFIG_FADS)
@@ -419,65 +338,6 @@ static inline int voltage_set(int slot, int vcc, int vpp)
#endif
-/* ------------------------------------------------------------------------- */
-/* Motorola MBX860 */
-
-#if defined(CONFIG_MBX)
-
-#define PCMCIA_BOARD_MSG "MBX"
-
-static int voltage_set(int slot, int vcc, int vpp)
-{
- u8 reg = 0;
-
- switch (vcc) {
- case 0:
- break;
- case 33:
- reg |= CSR2_VCC_33;
- break;
- case 50:
- reg |= CSR2_VCC_50;
- break;
- default:
- return 1;
- }
-
- switch (vpp) {
- case 0:
- break;
- case 33:
- case 50:
- if (vcc == vpp)
- reg |= CSR2_VPP_VCC;
- else
- return 1;
- break;
- case 120:
- if ((vcc == 33) || (vcc == 50))
- reg |= CSR2_VPP_12;
- else
- return 1;
- default:
- return 1;
- }
-
- /* first, turn off all power */
- out_8((u8 *) MBX_CSR2_ADDR,
- in_8((u8 *) MBX_CSR2_ADDR) & ~(CSR2_VCC_MASK | CSR2_VPP_MASK));
-
- /* enable new powersettings */
- out_8((u8 *) MBX_CSR2_ADDR, in_8((u8 *) MBX_CSR2_ADDR) | reg);
-
- return 0;
-}
-
-#define socket_get(_slot_) PCMCIA_SOCKET_KEY_5V
-#define hardware_enable(_slot_) /* No hardware to enable */
-#define hardware_disable(_slot_) /* No hardware to disable */
-
-#endif /* CONFIG_MBX */
-
#if defined(CONFIG_PRxK)
#include <asm/cpld.h>
extern volatile fpga_pc_regs *fpga_pc;
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 3338437b559b..85772616efbf 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -781,4 +781,12 @@ config APPLE_GMUX
graphics as well as the backlight. Currently only backlight
control is supported by the driver.
+config PVPANIC
+ tristate "pvpanic device support"
+ depends on ACPI
+ ---help---
+ This driver provides support for the pvpanic device. pvpanic is
+ a paravirtualized device provided by QEMU; it lets a virtual machine
+ (guest) communicate panic events to the host.
+
endif # X86_PLATFORM_DEVICES
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index ace2b38942fe..ef0ec746f78c 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -51,3 +51,5 @@ obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o
obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o
+
+obj-$(CONFIG_PVPANIC) += pvpanic.o
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index 210b5b872125..8fcb41e18b9c 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -171,6 +171,15 @@ static struct dmi_system_id asus_quirks[] = {
},
.driver_data = &quirk_asus_x401u,
},
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X75A",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X75A"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
{},
};
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index fa3ee6209572..1134119521ac 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -284,6 +284,7 @@ static void __init parse_da_table(const struct dmi_header *dm)
{
/* Final token is a terminator, so we don't want to copy it */
int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
+ struct calling_interface_token *new_da_tokens;
struct calling_interface_structure *table =
container_of(dm, struct calling_interface_structure, header);
@@ -296,12 +297,13 @@ static void __init parse_da_table(const struct dmi_header *dm)
da_command_address = table->cmdIOAddress;
da_command_code = table->cmdIOCode;
- da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
- sizeof(struct calling_interface_token),
- GFP_KERNEL);
+ new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
+ sizeof(struct calling_interface_token),
+ GFP_KERNEL);
- if (!da_tokens)
+ if (!new_da_tokens)
return;
+ da_tokens = new_da_tokens;
memcpy(da_tokens+da_num_tokens, table->tokens,
sizeof(struct calling_interface_token) * tokens);
diff --git a/drivers/platform/x86/dell-wmi-aio.c b/drivers/platform/x86/dell-wmi-aio.c
index 3f945457f71c..bcf8cc6b5537 100644
--- a/drivers/platform/x86/dell-wmi-aio.c
+++ b/drivers/platform/x86/dell-wmi-aio.c
@@ -34,6 +34,14 @@ MODULE_LICENSE("GPL");
#define EVENT_GUID1 "284A0E6B-380E-472A-921F-E52786257FB4"
#define EVENT_GUID2 "02314822-307C-4F66-BF0E-48AEAEB26CC8"
+struct dell_wmi_event {
+ u16 length;
+ /* 0x000: A hot key pressed or an event occurred
+ * 0x00F: A sequence of hot keys are pressed */
+ u16 type;
+ u16 event[];
+};
+
static const char *dell_wmi_aio_guids[] = {
EVENT_GUID1,
EVENT_GUID2,
@@ -46,15 +54,41 @@ MODULE_ALIAS("wmi:"EVENT_GUID2);
static const struct key_entry dell_wmi_aio_keymap[] = {
{ KE_KEY, 0xc0, { KEY_VOLUMEUP } },
{ KE_KEY, 0xc1, { KEY_VOLUMEDOWN } },
+ { KE_KEY, 0xe030, { KEY_VOLUMEUP } },
+ { KE_KEY, 0xe02e, { KEY_VOLUMEDOWN } },
+ { KE_KEY, 0xe020, { KEY_MUTE } },
+ { KE_KEY, 0xe027, { KEY_DISPLAYTOGGLE } },
+ { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
+ { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
+ { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
{ KE_END, 0 }
};
static struct input_dev *dell_wmi_aio_input_dev;
+/*
+ * The new WMI event data format will follow the dell_wmi_event structure
+ * So, we will check if the buffer matches the format
+ */
+static bool dell_wmi_aio_event_check(u8 *buffer, int length)
+{
+ struct dell_wmi_event *event = (struct dell_wmi_event *)buffer;
+
+ if (event == NULL || length < 6)
+ return false;
+
+ if ((event->type == 0 || event->type == 0xf) &&
+ event->length >= 2)
+ return true;
+
+ return false;
+}
+
static void dell_wmi_aio_notify(u32 value, void *context)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
+ struct dell_wmi_event *event;
acpi_status status;
status = wmi_get_event_data(value, &response);
@@ -65,7 +99,7 @@ static void dell_wmi_aio_notify(u32 value, void *context)
obj = (union acpi_object *)response.pointer;
if (obj) {
- unsigned int scancode;
+ unsigned int scancode = 0;
switch (obj->type) {
case ACPI_TYPE_INTEGER:
@@ -75,13 +109,22 @@ static void dell_wmi_aio_notify(u32 value, void *context)
scancode, 1, true);
break;
case ACPI_TYPE_BUFFER:
- /* Broken machines return the scancode in a buffer */
- if (obj->buffer.pointer && obj->buffer.length > 0) {
- scancode = obj->buffer.pointer[0];
+ if (dell_wmi_aio_event_check(obj->buffer.pointer,
+ obj->buffer.length)) {
+ event = (struct dell_wmi_event *)
+ obj->buffer.pointer;
+ scancode = event->event[0];
+ } else {
+ /* Broken machines return the scancode in a
+ buffer */
+ if (obj->buffer.pointer &&
+ obj->buffer.length > 0)
+ scancode = obj->buffer.pointer[0];
+ }
+ if (scancode)
sparse_keymap_report_event(
dell_wmi_aio_input_dev,
scancode, 1, true);
- }
break;
}
}
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index 1a779bbfb87d..8df0c5a21be2 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -71,6 +71,14 @@ enum hp_wmi_event_ids {
HPWMI_WIRELESS = 5,
HPWMI_CPU_BATTERY_THROTTLE = 6,
HPWMI_LOCK_SWITCH = 7,
+ HPWMI_LID_SWITCH = 8,
+ HPWMI_SCREEN_ROTATION = 9,
+ HPWMI_COOLSENSE_SYSTEM_MOBILE = 0x0A,
+ HPWMI_COOLSENSE_SYSTEM_HOT = 0x0B,
+ HPWMI_PROXIMITY_SENSOR = 0x0C,
+ HPWMI_BACKLIT_KB_BRIGHTNESS = 0x0D,
+ HPWMI_PEAKSHIFT_PERIOD = 0x0F,
+ HPWMI_BATTERY_CHARGE_PERIOD = 0x10,
};
struct bios_args {
@@ -536,6 +544,22 @@ static void hp_wmi_notify(u32 value, void *context)
break;
case HPWMI_LOCK_SWITCH:
break;
+ case HPWMI_LID_SWITCH:
+ break;
+ case HPWMI_SCREEN_ROTATION:
+ break;
+ case HPWMI_COOLSENSE_SYSTEM_MOBILE:
+ break;
+ case HPWMI_COOLSENSE_SYSTEM_HOT:
+ break;
+ case HPWMI_PROXIMITY_SENSOR:
+ break;
+ case HPWMI_BACKLIT_KB_BRIGHTNESS:
+ break;
+ case HPWMI_PEAKSHIFT_PERIOD:
+ break;
+ case HPWMI_BATTERY_CHARGE_PERIOD:
+ break;
default:
pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data);
break;
diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c
index e64a7a870d42..a8e43cf70fac 100644
--- a/drivers/platform/x86/hp_accel.c
+++ b/drivers/platform/x86/hp_accel.c
@@ -362,7 +362,8 @@ static int lis3lv02d_suspend(struct device *dev)
static int lis3lv02d_resume(struct device *dev)
{
- return lis3lv02d_poweron(&lis3_dev);
+ lis3lv02d_poweron(&lis3_dev);
+ return 0;
}
static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume);
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index 17f00b8dc5cb..89c4519d48ac 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -640,7 +640,8 @@ static void ideapad_check_special_buttons(struct ideapad_private *priv)
for (bit = 0; bit < 16; bit++) {
if (test_bit(bit, &value)) {
switch (bit) {
- case 6:
+ case 0: /* Z580 */
+ case 6: /* Z570 */
/* Thermal Management button */
ideapad_input_report(priv, 65);
break;
@@ -648,6 +649,9 @@ static void ideapad_check_special_buttons(struct ideapad_private *priv)
/* OneKey Theater button */
ideapad_input_report(priv, 64);
break;
+ default:
+ pr_info("Unknown special button: %lu\n", bit);
+ break;
}
}
}
diff --git a/drivers/platform/x86/pvpanic.c b/drivers/platform/x86/pvpanic.c
new file mode 100644
index 000000000000..47ae0c47d4b5
--- /dev/null
+++ b/drivers/platform/x86/pvpanic.c
@@ -0,0 +1,124 @@
+/*
+ * pvpanic.c - pvpanic Device Support
+ *
+ * Copyright (C) 2013 Fujitsu.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>");
+MODULE_DESCRIPTION("pvpanic device driver");
+MODULE_LICENSE("GPL");
+
+static int pvpanic_add(struct acpi_device *device);
+static int pvpanic_remove(struct acpi_device *device);
+
+static const struct acpi_device_id pvpanic_device_ids[] = {
+ { "QEMU0001", 0 },
+ { "", 0 },
+};
+MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids);
+
+#define PVPANIC_PANICKED (1 << 0)
+
+static u16 port;
+
+static struct acpi_driver pvpanic_driver = {
+ .name = "pvpanic",
+ .class = "QEMU",
+ .ids = pvpanic_device_ids,
+ .ops = {
+ .add = pvpanic_add,
+ .remove = pvpanic_remove,
+ },
+ .owner = THIS_MODULE,
+};
+
+static void
+pvpanic_send_event(unsigned int event)
+{
+ outb(event, port);
+}
+
+static int
+pvpanic_panic_notify(struct notifier_block *nb, unsigned long code,
+ void *unused)
+{
+ pvpanic_send_event(PVPANIC_PANICKED);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block pvpanic_panic_nb = {
+ .notifier_call = pvpanic_panic_notify,
+};
+
+
+static acpi_status
+pvpanic_walk_resources(struct acpi_resource *res, void *context)
+{
+ switch (res->type) {
+ case ACPI_RESOURCE_TYPE_END_TAG:
+ return AE_OK;
+
+ case ACPI_RESOURCE_TYPE_IO:
+ port = res->data.io.minimum;
+ return AE_OK;
+
+ default:
+ return AE_ERROR;
+ }
+}
+
+static int pvpanic_add(struct acpi_device *device)
+{
+ acpi_status status;
+ u64 ret;
+
+ status = acpi_evaluate_integer(device->handle, "_STA", NULL,
+ &ret);
+
+ if (ACPI_FAILURE(status) || (ret & 0x0B) != 0x0B)
+ return -ENODEV;
+
+ acpi_walk_resources(device->handle, METHOD_NAME__CRS,
+ pvpanic_walk_resources, NULL);
+
+ if (!port)
+ return -ENODEV;
+
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &pvpanic_panic_nb);
+
+ return 0;
+}
+
+static int pvpanic_remove(struct acpi_device *device)
+{
+
+ atomic_notifier_chain_unregister(&panic_notifier_list,
+ &pvpanic_panic_nb);
+ return 0;
+}
+
+module_acpi_driver(pvpanic_driver);
diff --git a/drivers/platform/x86/samsung-q10.c b/drivers/platform/x86/samsung-q10.c
index 5f770059fd4d..1a90b62a71c6 100644
--- a/drivers/platform/x86/samsung-q10.c
+++ b/drivers/platform/x86/samsung-q10.c
@@ -176,10 +176,7 @@ static int __init samsungq10_init(void)
samsungq10_probe,
NULL, 0, NULL, 0);
- if (IS_ERR(samsungq10_device))
- return PTR_ERR(samsungq10_device);
-
- return 0;
+ return PTR_RET(samsungq10_device);
}
static void __exit samsungq10_exit(void)
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index d544e3aaf761..2ac045f27f10 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -1255,6 +1255,11 @@ static void sony_nc_notify(struct acpi_device *device, u32 event)
real_ev = __sony_nc_gfx_switch_status_get();
break;
+ case 0x015B:
+ /* Hybrid GFX switching SVS151290S */
+ ev_type = GFX_SWITCH;
+ real_ev = __sony_nc_gfx_switch_status_get();
+ break;
default:
dprintk("Unknown event 0x%x for handle 0x%x\n",
event, handle);
@@ -1353,6 +1358,7 @@ static void sony_nc_function_setup(struct acpi_device *device,
break;
case 0x0128:
case 0x0146:
+ case 0x015B:
result = sony_nc_gfx_switch_setup(pf_device, handle);
if (result)
pr_err("couldn't set up GFX Switch status (%d)\n",
@@ -1375,6 +1381,7 @@ static void sony_nc_function_setup(struct acpi_device *device,
case 0x0143:
case 0x014b:
case 0x014c:
+ case 0x0163:
result = sony_nc_kbd_backlight_setup(pf_device, handle);
if (result)
pr_err("couldn't set up keyboard backlight function (%d)\n",
@@ -1426,6 +1433,7 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
break;
case 0x0128:
case 0x0146:
+ case 0x015B:
sony_nc_gfx_switch_cleanup(pd);
break;
case 0x0131:
@@ -1439,6 +1447,7 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
case 0x0143:
case 0x014b:
case 0x014c:
+ case 0x0163:
sony_nc_kbd_backlight_cleanup(pd);
break;
default:
@@ -1485,6 +1494,7 @@ static void sony_nc_function_resume(void)
case 0x0143:
case 0x014b:
case 0x014c:
+ case 0x0163:
sony_nc_kbd_backlight_resume();
break;
default:
@@ -2390,7 +2400,9 @@ static int __sony_nc_gfx_switch_status_get(void)
{
unsigned int result;
- if (sony_call_snc_handle(gfxs_ctl->handle, 0x0100, &result))
+ if (sony_call_snc_handle(gfxs_ctl->handle,
+ gfxs_ctl->handle == 0x015B ? 0x0000 : 0x0100,
+ &result))
return -EIO;
switch (gfxs_ctl->handle) {
@@ -2400,6 +2412,12 @@ static int __sony_nc_gfx_switch_status_get(void)
*/
return result & 0x1 ? SPEED : STAMINA;
break;
+ case 0x015B:
+ /* 0: discrete GFX (speed)
+ * 1: integrated GFX (stamina)
+ */
+ return result & 0x1 ? STAMINA : SPEED;
+ break;
case 0x0128:
/* it's a more elaborated bitmask, for now:
* 2: integrated GFX (stamina)
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index db95c547c09d..86af29f53bbe 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -1353,6 +1353,8 @@ config SCSI_LPFC
tristate "Emulex LightPulse Fibre Channel Support"
depends on PCI && SCSI
select SCSI_FC_ATTRS
+ select GENERIC_CSUM
+ select CRC_T10DIF
help
This lpfc driver supports the Emulex LightPulse
Family of Fibre Channel PCI host adapters.
diff --git a/drivers/scsi/aic94xx/aic94xx_dev.c b/drivers/scsi/aic94xx/aic94xx_dev.c
index 64136c56e706..33072388ea16 100644
--- a/drivers/scsi/aic94xx/aic94xx_dev.c
+++ b/drivers/scsi/aic94xx/aic94xx_dev.c
@@ -84,7 +84,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);
@@ -116,7 +116,7 @@ void asd_set_dmamode(struct domain_device *dev)
int ddb = (int) (unsigned long) dev->lldd_dev;
u32 qdepth = 0;
- if (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT) {
+ 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,
@@ -140,8 +140,8 @@ static int asd_init_sata(struct domain_device *dev)
int ddb = (int) (unsigned long) dev->lldd_dev;
asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF);
- if (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM ||
- dev->dev_type == SATA_PM_PORT) {
+ 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);
@@ -174,7 +174,7 @@ 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;
if (rps_resp->frame_type == SMP_RESPONSE &&
@@ -188,8 +188,8 @@ 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))
+ (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE ||
+ dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE))
asd_ddbsite_write_byte(asd_ha, ddb, MAX_CCONN,
4);
else
@@ -198,7 +198,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);
@@ -211,7 +211,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);
@@ -219,7 +219,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,
@@ -328,10 +328,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_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c
index 81b736c76fff..4df867e07b20 100644
--- a/drivers/scsi/aic94xx/aic94xx_hwi.c
+++ b/drivers/scsi/aic94xx/aic94xx_hwi.c
@@ -74,7 +74,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)
diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c
index cf9040933da6..d4c35df3d4ae 100644
--- a/drivers/scsi/aic94xx/aic94xx_tmf.c
+++ b/drivers/scsi/aic94xx/aic94xx_tmf.c
@@ -184,7 +184,7 @@ int asd_I_T_nexus_reset(struct domain_device *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);
diff --git a/drivers/scsi/be2iscsi/be.h b/drivers/scsi/be2iscsi/be.h
index f1733dfa3ae2..777e7c0bbb4b 100644
--- a/drivers/scsi/be2iscsi/be.h
+++ b/drivers/scsi/be2iscsi/be.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2012 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c
index 5c87768c109c..e66aa7c11a8a 100644
--- a/drivers/scsi/be2iscsi/be_cmds.c
+++ b/drivers/scsi/be2iscsi/be_cmds.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2012 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -155,6 +155,7 @@ int beiscsi_mccq_compl(struct beiscsi_hba *phba,
uint16_t status = 0, addl_status = 0, wrb_num = 0;
struct be_mcc_wrb *temp_wrb;
struct be_cmd_req_hdr *ioctl_hdr;
+ struct be_cmd_resp_hdr *ioctl_resp_hdr;
struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
if (beiscsi_error(phba))
@@ -204,6 +205,12 @@ int beiscsi_mccq_compl(struct beiscsi_hba *phba,
ioctl_hdr->subsystem,
ioctl_hdr->opcode,
status, addl_status);
+
+ if (status == MCC_STATUS_INSUFFICIENT_BUFFER) {
+ ioctl_resp_hdr = (struct be_cmd_resp_hdr *) ioctl_hdr;
+ if (ioctl_resp_hdr->response_length)
+ goto release_mcc_tag;
+ }
rc = -EAGAIN;
}
@@ -267,6 +274,7 @@ static int be_mcc_compl_process(struct be_ctrl_info *ctrl,
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);
+ struct be_cmd_resp_hdr *resp_hdr;
be_dws_le_to_cpu(compl, 4);
@@ -284,6 +292,11 @@ static int be_mcc_compl_process(struct be_ctrl_info *ctrl,
hdr->subsystem, hdr->opcode,
compl_status, extd_status);
+ if (compl_status == MCC_STATUS_INSUFFICIENT_BUFFER) {
+ resp_hdr = (struct be_cmd_resp_hdr *) hdr;
+ if (resp_hdr->response_length)
+ return 0;
+ }
return -EBUSY;
}
return 0;
@@ -335,30 +348,26 @@ static void be2iscsi_fail_session(struct iscsi_cls_session *cls_session)
void beiscsi_async_link_state_process(struct beiscsi_hba *phba,
struct be_async_event_link_state *evt)
{
- switch (evt->port_link_status) {
- case ASYNC_EVENT_LINK_DOWN:
+ if ((evt->port_link_status == ASYNC_EVENT_LINK_DOWN) ||
+ ((evt->port_link_status & ASYNC_EVENT_LOGICAL) &&
+ (evt->port_fault != BEISCSI_PHY_LINK_FAULT_NONE))) {
+ phba->state = BE_ADAPTER_LINK_DOWN;
+
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_INIT,
- "BC_%d : Link Down on Physical Port %d\n",
+ "BC_%d : Link Down on Port %d\n",
evt->physical_port);
- phba->state |= BE_ADAPTER_LINK_DOWN;
iscsi_host_for_each_session(phba->shost,
be2iscsi_fail_session);
- break;
- case ASYNC_EVENT_LINK_UP:
+ } else if ((evt->port_link_status & ASYNC_EVENT_LINK_UP) ||
+ ((evt->port_link_status & ASYNC_EVENT_LOGICAL) &&
+ (evt->port_fault == BEISCSI_PHY_LINK_FAULT_NONE))) {
phba->state = BE_ADAPTER_UP;
+
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_INIT,
- "BC_%d : Link UP on Physical Port %d\n",
- evt->physical_port);
- break;
- default:
- beiscsi_log(phba, KERN_ERR,
- BEISCSI_LOG_CONFIG | BEISCSI_LOG_INIT,
- "BC_%d : Unexpected Async Notification %d on"
- "Physical Port %d\n",
- evt->port_link_status,
+ "BC_%d : Link UP on Port %d\n",
evt->physical_port);
}
}
@@ -479,7 +488,7 @@ static int be_mbox_db_ready_wait(struct be_ctrl_info *ctrl)
{
void __iomem *db = ctrl->db + MPU_MAILBOX_DB_OFFSET;
struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
- int wait = 0;
+ uint32_t wait = 0;
u32 ready;
do {
@@ -527,6 +536,10 @@ int be_mbox_notify(struct be_ctrl_info *ctrl)
struct be_mcc_compl *compl = &mbox->compl;
struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
+ status = be_mbox_db_ready_wait(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;
@@ -580,6 +593,10 @@ static int be_mbox_notify_wait(struct beiscsi_hba *phba)
struct be_mcc_compl *compl = &mbox->compl;
struct be_ctrl_info *ctrl = &phba->ctrl;
+ status = be_mbox_db_ready_wait(ctrl);
+ if (status)
+ return status;
+
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;
@@ -732,6 +749,16 @@ int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl,
return status;
}
+/**
+ * be_cmd_fw_initialize()- Initialize FW
+ * @ctrl: Pointer to function control structure
+ *
+ * Send FW initialize pattern for the function.
+ *
+ * return
+ * Success: 0
+ * Failure: Non-Zero value
+ **/
int be_cmd_fw_initialize(struct be_ctrl_info *ctrl)
{
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
@@ -762,6 +789,47 @@ int be_cmd_fw_initialize(struct be_ctrl_info *ctrl)
return status;
}
+/**
+ * be_cmd_fw_uninit()- Uinitialize FW
+ * @ctrl: Pointer to function control structure
+ *
+ * Send FW uninitialize pattern for the function
+ *
+ * return
+ * Success: 0
+ * Failure: Non-Zero value
+ **/
+int be_cmd_fw_uninit(struct be_ctrl_info *ctrl)
+{
+ struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
+ int status;
+ u8 *endian_check;
+
+ spin_lock(&ctrl->mbox_lock);
+ memset(wrb, 0, sizeof(*wrb));
+
+ endian_check = (u8 *) wrb;
+ *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_ERR, BEISCSI_LOG_INIT,
+ "BC_%d : be_cmd_fw_uninit Failed\n");
+
+ spin_unlock(&ctrl->mbox_lock);
+ return status;
+}
+
int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
struct be_queue_info *cq, struct be_queue_info *eq,
bool sol_evts, bool no_delay, int coalesce_wm)
@@ -783,20 +851,7 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
OPCODE_COMMON_CQ_CREATE, sizeof(*req));
req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
- if (chip_skh_r(ctrl->pdev)) {
- 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);
- } else {
+ 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);
@@ -809,6 +864,19 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
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);
}
be_dws_cpu_to_le(ctxt, sizeof(req->context));
@@ -949,6 +1017,7 @@ int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl,
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;
@@ -961,17 +1030,36 @@ 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 (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));
diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h
index 23397d51ac54..99073086dfe0 100644
--- a/drivers/scsi/be2iscsi/be_cmds.h
+++ b/drivers/scsi/be2iscsi/be_cmds.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2012 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -52,6 +52,10 @@ struct be_mcc_wrb {
/* Completion Status */
#define MCC_STATUS_SUCCESS 0x0
+#define MCC_STATUS_FAILED 0x1
+#define MCC_STATUS_ILLEGAL_REQUEST 0x2
+#define MCC_STATUS_ILLEGAL_FIELD 0x3
+#define MCC_STATUS_INSUFFICIENT_BUFFER 0x4
#define CQE_STATUS_COMPL_MASK 0xFFFF
#define CQE_STATUS_COMPL_SHIFT 0 /* bits 0 - 15 */
@@ -118,7 +122,8 @@ struct be_async_event_trailer {
enum {
ASYNC_EVENT_LINK_DOWN = 0x0,
- ASYNC_EVENT_LINK_UP = 0x1
+ ASYNC_EVENT_LINK_UP = 0x1,
+ ASYNC_EVENT_LOGICAL = 0x2
};
/**
@@ -130,6 +135,9 @@ struct be_async_event_link_state {
u8 port_link_status;
u8 port_duplex;
u8 port_speed;
+#define BEISCSI_PHY_LINK_FAULT_NONE 0x00
+#define BEISCSI_PHY_LINK_FAULT_LOCAL 0x01
+#define BEISCSI_PHY_LINK_FAULT_REMOTE 0x02
u8 port_fault;
u8 rsvd0[7];
struct be_async_event_trailer trailer;
@@ -697,6 +705,7 @@ int beiscsi_mccq_compl(struct beiscsi_hba *phba,
uint32_t tag, struct be_mcc_wrb **wrb, void *cmd_va);
/*ISCSI Functuions */
int be_cmd_fw_initialize(struct be_ctrl_info *ctrl);
+int be_cmd_fw_uninit(struct be_ctrl_info *ctrl);
struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem);
struct be_mcc_wrb *wrb_from_mccq(struct beiscsi_hba *phba);
@@ -751,6 +760,18 @@ 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;
@@ -896,7 +917,7 @@ struct amap_it_dmsg_cqe_v2 {
* 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
diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c
index 9014690fe841..ef36be003f67 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.c
+++ b/drivers/scsi/be2iscsi/be_iscsi.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2012 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -161,7 +161,9 @@ static int beiscsi_bindconn_cid(struct beiscsi_hba *phba,
struct beiscsi_conn *beiscsi_conn,
unsigned int cid)
{
- if (phba->conn_table[cid]) {
+ uint16_t cri_index = BE_GET_CRI_FROM_CID(cid);
+
+ if (phba->conn_table[cri_index]) {
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
"BS_%d : Connection table already occupied. Detected clash\n");
@@ -169,9 +171,9 @@ static int beiscsi_bindconn_cid(struct beiscsi_hba *phba,
} else {
beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
"BS_%d : phba->conn_table[%d]=%p(beiscsi_conn)\n",
- cid, beiscsi_conn);
+ cri_index, beiscsi_conn);
- phba->conn_table[cid] = beiscsi_conn;
+ phba->conn_table[cri_index] = beiscsi_conn;
}
return 0;
}
@@ -990,9 +992,27 @@ static void beiscsi_put_cid(struct beiscsi_hba *phba, unsigned short cid)
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;
+ 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;
+ if (beiscsi_conn->login_in_progress) {
+ beiscsi_free_mgmt_task_handles(beiscsi_conn,
+ beiscsi_conn->task);
+ beiscsi_conn->login_in_progress = 0;
+ }
}
/**
@@ -1009,7 +1029,6 @@ 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_mcc_wrb *wrb;
struct tcp_connect_and_offload_out *ptcpcnct_out;
struct be_dma_mem nonemb_cmd;
unsigned int tag;
@@ -1029,15 +1048,8 @@ static int beiscsi_open_conn(struct iscsi_endpoint *ep,
"BS_%d : 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)) {
-
- beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
- "BS_%d : Failed in allocate iscsi cid\n");
- goto free_ep;
- }
+ 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,
@@ -1049,24 +1061,24 @@ static int beiscsi_open_conn(struct iscsi_endpoint *ep,
"BS_%d : Failed to allocate memory for"
" mgmt_open_connection\n");
- beiscsi_put_cid(phba, beiscsi_ep->ep_cid);
+ beiscsi_free_ep(beiscsi_ep);
return -ENOMEM;
}
nonemb_cmd.size = sizeof(struct tcp_connect_and_offload_in);
memset(nonemb_cmd.va, 0, nonemb_cmd.size);
tag = mgmt_open_connection(phba, dst_addr, beiscsi_ep, &nonemb_cmd);
- if (!tag) {
+ if (tag <= 0) {
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
"BS_%d : 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,
nonemb_cmd.va, nonemb_cmd.dma);
+ beiscsi_free_ep(beiscsi_ep);
return -EAGAIN;
}
- ret = beiscsi_mccq_compl(phba, tag, &wrb, NULL);
+ ret = beiscsi_mccq_compl(phba, tag, NULL, nonemb_cmd.va);
if (ret) {
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
@@ -1074,10 +1086,11 @@ static int beiscsi_open_conn(struct iscsi_endpoint *ep,
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
- goto free_ep;
+ beiscsi_free_ep(beiscsi_ep);
+ return -EBUSY;
}
- ptcpcnct_out = embedded_payload(wrb);
+ 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;
@@ -1087,10 +1100,6 @@ static int beiscsi_open_conn(struct iscsi_endpoint *ep,
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
return 0;
-
-free_ep:
- beiscsi_free_ep(beiscsi_ep);
- return -EBUSY;
}
/**
@@ -1119,6 +1128,13 @@ beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
return ERR_PTR(ret);
}
+ if (beiscsi_error(phba)) {
+ ret = -EIO;
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG,
+ "BS_%d : The FW state Not Stable!!!\n");
+ return ERR_PTR(ret);
+ }
+
if (phba->state != BE_ADAPTER_UP) {
ret = -EBUSY;
beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG,
@@ -1201,8 +1217,10 @@ static int beiscsi_close_conn(struct beiscsi_endpoint *beiscsi_ep, int flag)
static int beiscsi_unbind_conn_to_cid(struct beiscsi_hba *phba,
unsigned int cid)
{
- if (phba->conn_table[cid])
- phba->conn_table[cid] = NULL;
+ uint16_t cri_index = BE_GET_CRI_FROM_CID(cid);
+
+ if (phba->conn_table[cri_index])
+ phba->conn_table[cri_index] = NULL;
else {
beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
"BS_%d : Connection table Not occupied.\n");
diff --git a/drivers/scsi/be2iscsi/be_iscsi.h b/drivers/scsi/be2iscsi/be_iscsi.h
index 38eab7232159..31ddc8494398 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.h
+++ b/drivers/scsi/be2iscsi/be_iscsi.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2012 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
index 4e2733d23003..d24a2867bc21 100644
--- a/drivers/scsi/be2iscsi/be_main.c
+++ b/drivers/scsi/be2iscsi/be_main.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2012 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -153,10 +153,14 @@ BEISCSI_RW_ATTR(log_enable, 0x00,
DEVICE_ATTR(beiscsi_drvr_ver, S_IRUGO, beiscsi_drvr_ver_disp, NULL);
DEVICE_ATTR(beiscsi_adapter_family, S_IRUGO, beiscsi_adap_family_disp, NULL);
+DEVICE_ATTR(beiscsi_fw_ver, S_IRUGO, beiscsi_fw_ver_disp, NULL);
+DEVICE_ATTR(beiscsi_active_cid_count, S_IRUGO, beiscsi_active_cid_disp, NULL);
struct device_attribute *beiscsi_attrs[] = {
&dev_attr_beiscsi_log_enable,
&dev_attr_beiscsi_drvr_ver,
&dev_attr_beiscsi_adapter_family,
+ &dev_attr_beiscsi_fw_ver,
+ &dev_attr_beiscsi_active_cid_count,
NULL,
};
@@ -702,7 +706,7 @@ static void beiscsi_get_params(struct beiscsi_hba *phba)
+ 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.asyncpdus_per_ctrl = phba->fw_config.iscsi_cid_count;
phba->params.icds_per_ctrl = phba->fw_config.iscsi_icd_count;
phba->params.num_sge_per_io = BE2_SGE;
phba->params.defpdu_hdr_sz = BE2_DEFPDU_HDR_SZ;
@@ -1032,7 +1036,6 @@ static void hwi_ring_cq_db(struct beiscsi_hba *phba,
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)
@@ -1144,9 +1147,10 @@ struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid)
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];
+ pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
if (pwrb_context->wrb_handles_available >= 2) {
pwrb_handle = pwrb_context->pwrb_handle_base[
pwrb_context->alloc_index];
@@ -1322,8 +1326,9 @@ be_complete_logout(struct beiscsi_conn *beiscsi_conn,
hdr->t2retain = 0;
hdr->flags = csol_cqe->i_flags;
hdr->response = csol_cqe->i_resp;
- hdr->exp_cmdsn = csol_cqe->exp_cmdsn;
- hdr->max_cmdsn = (csol_cqe->exp_cmdsn + csol_cqe->cmd_wnd - 1);
+ 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;
@@ -1346,9 +1351,9 @@ be_complete_tmf(struct beiscsi_conn *beiscsi_conn,
hdr->opcode = ISCSI_OP_SCSI_TMFUNC_RSP;
hdr->flags = csol_cqe->i_flags;
hdr->response = csol_cqe->i_resp;
- hdr->exp_cmdsn = csol_cqe->exp_cmdsn;
- hdr->max_cmdsn = (csol_cqe->exp_cmdsn +
- csol_cqe->cmd_wnd - 1);
+ 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);
@@ -1363,35 +1368,29 @@ hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn,
struct hwi_controller *phwi_ctrlr;
struct iscsi_task *task;
struct beiscsi_io_task *io_task;
- struct iscsi_conn *conn = beiscsi_conn->conn;
- struct iscsi_session *session = conn->session;
- uint16_t wrb_index, cid;
+ uint16_t wrb_index, cid, cri_index;
phwi_ctrlr = phba->phwi_ctrlr;
- if (chip_skh_r(phba->pcidev)) {
- wrb_index = AMAP_GET_BITS(struct amap_it_dmsg_cqe_v2,
+ 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_v2,
+ cid = AMAP_GET_BITS(struct amap_it_dmsg_cqe,
cid, psol);
} else {
- wrb_index = AMAP_GET_BITS(struct amap_it_dmsg_cqe,
+ wrb_index = AMAP_GET_BITS(struct amap_it_dmsg_cqe_v2,
wrb_idx, psol);
- cid = AMAP_GET_BITS(struct amap_it_dmsg_cqe,
+ cid = AMAP_GET_BITS(struct amap_it_dmsg_cqe_v2,
cid, psol);
}
- pwrb_context = &phwi_ctrlr->wrb_context[
- cid - phba->fw_config.iscsi_cid_start];
+ 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];
task = pwrb_handle->pio_handle;
io_task = task->dd_data;
- spin_lock_bh(&phba->mgmt_sgl_lock);
- free_mgmt_sgl_handle(phba, io_task->psgl_handle);
- spin_unlock_bh(&phba->mgmt_sgl_lock);
- spin_lock_bh(&session->lock);
- free_wrb_handle(phba, pwrb_context, pwrb_handle);
- spin_unlock_bh(&session->lock);
+ memset(io_task->pwrb_handle->pwrb, 0, sizeof(struct iscsi_wrb));
+ iscsi_put_task(task);
}
static void
@@ -1406,8 +1405,8 @@ be_complete_nopin_resp(struct beiscsi_conn *beiscsi_conn,
hdr = (struct iscsi_nopin *)task->hdr;
hdr->flags = csol_cqe->i_flags;
hdr->exp_cmdsn = cpu_to_be32(csol_cqe->exp_cmdsn);
- hdr->max_cmdsn = be32_to_cpu(hdr->exp_cmdsn +
- csol_cqe->cmd_wnd - 1);
+ 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;
@@ -1418,7 +1417,26 @@ static void adapter_get_sol_cqe(struct beiscsi_hba *phba,
struct sol_cqe *psol,
struct common_sol_cqe *csol_cqe)
{
- if (chip_skh_r(phba->pcidev)) {
+ 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,
@@ -1429,7 +1447,7 @@ static void adapter_get_sol_cqe(struct beiscsi_hba *phba,
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,
+ 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))
@@ -1445,25 +1463,6 @@ static void adapter_get_sol_cqe(struct beiscsi_hba *phba,
if (AMAP_GET_BITS(struct amap_sol_cqe_v2,
o, psol))
csol_cqe->i_flags |= ISCSI_FLAG_CMD_OVERFLOW;
- } else {
- 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);
}
}
@@ -1480,14 +1479,15 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
struct iscsi_conn *conn = beiscsi_conn->conn;
struct iscsi_session *session = conn->session;
struct common_sol_cqe csol_cqe = {0};
+ uint16_t cri_index = 0;
phwi_ctrlr = phba->phwi_ctrlr;
/* Copy the elements to a common structure */
adapter_get_sol_cqe(phba, psol, &csol_cqe);
- pwrb_context = &phwi_ctrlr->wrb_context[
- csol_cqe.cid - phba->fw_config.iscsi_cid_start];
+ 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];
@@ -1561,15 +1561,15 @@ hwi_get_async_handle(struct beiscsi_hba *phba,
unsigned char is_header = 0;
unsigned int index, dpl;
- if (chip_skh_r(phba->pcidev)) {
- dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
+ if (is_chip_be2_be3r(phba)) {
+ dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
dpl, pdpdu_cqe);
- index = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
+ index = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
index, pdpdu_cqe);
} else {
- dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
+ dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
dpl, pdpdu_cqe);
- index = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
+ index = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
index, pdpdu_cqe);
}
@@ -1613,8 +1613,8 @@ hwi_get_async_handle(struct beiscsi_hba *phba,
WARN_ON(!pasync_handle);
- pasync_handle->cri = (unsigned short)beiscsi_conn->beiscsi_conn_cid -
- phba->fw_config.iscsi_cid_start;
+ pasync_handle->cri =
+ BE_GET_CRI_FROM_CID(beiscsi_conn->beiscsi_conn_cid);
pasync_handle->is_header = is_header;
pasync_handle->buffer_len = dpl;
*pcq_index = index;
@@ -1856,8 +1856,6 @@ hwi_fwd_async_msg(struct beiscsi_conn *beiscsi_conn,
}
status = beiscsi_process_async_pdu(beiscsi_conn, phba,
- (beiscsi_conn->beiscsi_conn_cid -
- phba->fw_config.iscsi_cid_start),
phdr, hdr_len, pfirst_buffer,
offset);
@@ -2011,6 +2009,7 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
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;
@@ -2028,7 +2027,9 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
32] & CQE_CODE_MASK);
/* Get the CID */
- if (chip_skh_r(phba->pcidev)) {
+ 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))
@@ -2038,10 +2039,10 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
else
cid = AMAP_GET_BITS(struct amap_sol_cqe_v2,
cid, sol);
- } else
- cid = AMAP_GET_BITS(struct amap_sol_cqe, cid, sol);
+ }
- ep = phba->ep_array[cid - phba->fw_config.iscsi_cid_start];
+ cri_index = BE_GET_CRI_FROM_CID(cid);
+ ep = phba->ep_array[cri_index];
beiscsi_ep = ep->dd_data;
beiscsi_conn = beiscsi_ep->conn;
@@ -2191,7 +2192,7 @@ void beiscsi_process_all_cqs(struct work_struct *work)
static int be_iopoll(struct blk_iopoll *iop, int budget)
{
- static unsigned int ret;
+ unsigned int ret;
struct beiscsi_hba *phba;
struct be_eq_obj *pbe_eq;
@@ -2416,11 +2417,11 @@ static void hwi_write_buffer(struct iscsi_wrb *pwrb, struct iscsi_task *task)
/* Check for the data_count */
dsp_value = (task->data_count) ? 1 : 0;
- if (chip_skh_r(phba->pcidev))
- AMAP_SET_BITS(struct amap_iscsi_wrb_v2, dsp,
+ 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, dsp,
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, dsp,
pwrb, dsp_value);
/* Map addr only if there is data_count */
@@ -2538,8 +2539,9 @@ static void beiscsi_find_mem_req(struct beiscsi_hba *phba)
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;
@@ -2547,9 +2549,18 @@ static int beiscsi_alloc_mem(struct beiscsi_hba *phba)
if (!phba->phwi_ctrlr)
return -ENOMEM;
+ /* Allocate memory for wrb_context */
+ phwi_ctrlr = phba->phwi_ctrlr;
+ phwi_ctrlr->wrb_context = kzalloc(sizeof(struct hwi_wrb_context) *
+ phba->params.cxns_per_ctrl,
+ GFP_KERNEL);
+ if (!phwi_ctrlr->wrb_context)
+ 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;
}
@@ -2558,6 +2569,7 @@ static int beiscsi_alloc_mem(struct beiscsi_hba *phba)
GFP_KERNEL);
if (!mem_arr_orig) {
kfree(phba->init_mem);
+ kfree(phwi_ctrlr->wrb_context);
kfree(phba->phwi_ctrlr);
return -ENOMEM;
}
@@ -2628,6 +2640,7 @@ free_mem:
}
kfree(mem_arr_orig);
kfree(phba->init_mem);
+ kfree(phba->phwi_ctrlr->wrb_context);
kfree(phba->phwi_ctrlr);
return -ENOMEM;
}
@@ -2666,6 +2679,7 @@ static void iscsi_init_global_templates(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 hwi_context_memory *phwi_ctxt;
struct wrb_handle *pwrb_handle = NULL;
struct hwi_controller *phwi_ctrlr;
struct hwi_wrb_context *pwrb_context;
@@ -2680,7 +2694,18 @@ static int beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
mem_descr_wrb += HWI_MEM_WRB;
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 = kzalloc(sizeof(struct be_queue_info) *
+ phba->fw_config.iscsi_cid_count,
+ 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 *) *
@@ -2723,7 +2748,7 @@ static int beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
}
}
idx = 0;
- 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) {
pwrb = mem_descr_wrb->mem_array[idx].virtual_address;
@@ -2752,7 +2777,7 @@ init_wrb_hndl_failed:
return -ENOMEM;
}
-static void hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
+static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
{
struct hwi_controller *phwi_ctrlr;
struct hba_parameters *p = &phba->params;
@@ -2770,6 +2795,15 @@ static void hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx;
memset(pasync_ctx, 0, sizeof(*pasync_ctx));
+ pasync_ctx->async_entry = kzalloc(sizeof(struct hwi_async_entry) *
+ phba->fw_config.iscsi_cid_count,
+ GFP_KERNEL);
+ if (!pasync_ctx->async_entry) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : hwi_init_async_pdu_ctx Mem Alloc Failed\n");
+ return -ENOMEM;
+ }
+
pasync_ctx->num_entries = p->asyncpdus_per_ctrl;
pasync_ctx->buffer_size = p->defpdu_hdr_sz;
@@ -2934,6 +2968,8 @@ static void hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
pasync_ctx->async_header.ep_read_ptr = -1;
pasync_ctx->async_data.host_write_ptr = 0;
pasync_ctx->async_data.ep_read_ptr = -1;
+
+ return 0;
}
static int
@@ -3293,6 +3329,7 @@ beiscsi_create_wrb_rings(struct beiscsi_hba *phba,
void *wrb_vaddr;
struct be_dma_mem sgl;
struct be_mem_descriptor *mem_descr;
+ struct hwi_wrb_context *pwrb_context;
int status;
idx = 0;
@@ -3351,8 +3388,9 @@ beiscsi_create_wrb_rings(struct beiscsi_hba *phba,
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];
+ pwrb_context->cid = phwi_context->be_wrbq[i].id;
+ BE_SET_CID_TO_CRI(i, pwrb_context->cid);
}
kfree(pwrb_arr);
return 0;
@@ -3365,7 +3403,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);
@@ -3394,6 +3432,7 @@ static void hwi_cleanup(struct beiscsi_hba *phba)
struct be_ctrl_info *ctrl = &phba->ctrl;
struct hwi_controller *phwi_ctrlr;
struct hwi_context_memory *phwi_context;
+ struct hwi_async_pdu_context *pasync_ctx;
int i, eq_num;
phwi_ctrlr = phba->phwi_ctrlr;
@@ -3403,6 +3442,7 @@ static void hwi_cleanup(struct beiscsi_hba *phba)
if (q->created)
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_WRBQ);
}
+ kfree(phwi_context->be_wrbq);
free_wrb_handles(phba);
q = &phwi_context->be_def_hdrq;
@@ -3430,6 +3470,10 @@ static void hwi_cleanup(struct beiscsi_hba *phba)
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ);
}
be_mcc_queues_destroy(phba);
+
+ pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx;
+ kfree(pasync_ctx->async_entry);
+ be_cmd_fw_uninit(ctrl);
}
static int be_mcc_queues_create(struct beiscsi_hba *phba,
@@ -3607,7 +3651,12 @@ static int hwi_init_controller(struct beiscsi_hba *phba)
if (beiscsi_init_wrb_handle(phba))
return -ENOMEM;
- hwi_init_async_pdu_ctx(phba);
+ 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) {
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
"BM_%d : hwi_init_controller failed\n");
@@ -3637,6 +3686,7 @@ static void beiscsi_free_mem(struct beiscsi_hba *phba)
mem_descr++;
}
kfree(phba->init_mem);
+ kfree(phba->phwi_ctrlr->wrb_context);
kfree(phba->phwi_ctrlr);
}
@@ -3769,7 +3819,7 @@ static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba)
static int hba_setup_cid_tbls(struct beiscsi_hba *phba)
{
- int i, new_cid;
+ int i;
phba->cid_array = kzalloc(sizeof(void *) * phba->params.cxns_per_ctrl,
GFP_KERNEL);
@@ -3780,19 +3830,33 @@ static int hba_setup_cid_tbls(struct beiscsi_hba *phba)
return -ENOMEM;
}
phba->ep_array = kzalloc(sizeof(struct iscsi_endpoint *) *
- phba->params.cxns_per_ctrl * 2, GFP_KERNEL);
+ phba->params.cxns_per_ctrl, GFP_KERNEL);
if (!phba->ep_array) {
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
"BM_%d : Failed to allocate memory in "
"hba_setup_cid_tbls\n");
kfree(phba->cid_array);
+ phba->cid_array = NULL;
return -ENOMEM;
}
- new_cid = phba->fw_config.iscsi_cid_start;
- for (i = 0; i < phba->params.cxns_per_ctrl; i++) {
- phba->cid_array[i] = new_cid;
- new_cid += 2;
+
+ phba->conn_table = kzalloc(sizeof(struct beiscsi_conn *) *
+ phba->params.cxns_per_ctrl, GFP_KERNEL);
+ if (!phba->conn_table) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : Failed to allocate memory in"
+ "hba_setup_cid_tbls\n");
+
+ kfree(phba->cid_array);
+ kfree(phba->ep_array);
+ phba->cid_array = NULL;
+ phba->ep_array = NULL;
+ return -ENOMEM;
}
+
+ for (i = 0; i < phba->params.cxns_per_ctrl; i++)
+ phba->cid_array[i] = phba->phwi_ctrlr->wrb_context[i].cid;
+
phba->avlbl_cids = phba->params.cxns_per_ctrl;
return 0;
}
@@ -4062,6 +4126,53 @@ static void beiscsi_clean_port(struct beiscsi_hba *phba)
kfree(phba->eh_sgl_hndl_base);
kfree(phba->cid_array);
kfree(phba->ep_array);
+ kfree(phba->conn_table);
+}
+
+/**
+ * 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;
+ uint16_t cri_index = BE_GET_CRI_FROM_CID(
+ beiscsi_conn->beiscsi_conn_cid);
+
+ phwi_ctrlr = phba->phwi_ctrlr;
+ pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
+
+ io_task = task->dd_data;
+
+ if (io_task->pwrb_handle) {
+ memset(io_task->pwrb_handle->pwrb, 0,
+ sizeof(struct iscsi_wrb));
+ free_wrb_handle(phba, pwrb_context,
+ io_task->pwrb_handle);
+ io_task->pwrb_handle = NULL;
+ }
+
+ if (io_task->psgl_handle) {
+ spin_lock_bh(&phba->mgmt_sgl_lock);
+ free_mgmt_sgl_handle(phba,
+ io_task->psgl_handle);
+ io_task->psgl_handle = NULL;
+ spin_unlock_bh(&phba->mgmt_sgl_lock);
+ }
+
+ if (io_task->mtask_addr)
+ pci_unmap_single(phba->pcidev,
+ io_task->mtask_addr,
+ io_task->mtask_data_count,
+ PCI_DMA_TODEVICE);
}
/**
@@ -4078,10 +4189,11 @@ static void beiscsi_cleanup_task(struct iscsi_task *task)
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);
phwi_ctrlr = phba->phwi_ctrlr;
- pwrb_context = &phwi_ctrlr->wrb_context[beiscsi_conn->beiscsi_conn_cid
- - phba->fw_config.iscsi_cid_start];
+ pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
if (io_task->cmd_bhs) {
pci_pool_free(beiscsi_sess->bhs_pool, io_task->cmd_bhs,
@@ -4103,27 +4215,8 @@ static void beiscsi_cleanup_task(struct iscsi_task *task)
io_task->psgl_handle = NULL;
}
} else {
- if (!beiscsi_conn->login_in_progress) {
- 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) {
- 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;
- }
- if (io_task->mtask_addr) {
- pci_unmap_single(phba->pcidev,
- io_task->mtask_addr,
- io_task->mtask_data_count,
- PCI_DMA_TODEVICE);
- io_task->mtask_addr = 0;
- }
- }
+ if (!beiscsi_conn->login_in_progress)
+ beiscsi_free_mgmt_task_handles(beiscsi_conn, task);
}
}
@@ -4146,15 +4239,14 @@ beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn,
beiscsi_cleanup_task(task);
spin_unlock_bh(&session->lock);
- pwrb_handle = alloc_wrb_handle(phba, (beiscsi_conn->beiscsi_conn_cid -
- phba->fw_config.iscsi_cid_start));
+ pwrb_handle = alloc_wrb_handle(phba, beiscsi_conn->beiscsi_conn_cid);
/* Check for the adapter family */
- if (chip_skh_r(phba->pcidev))
- beiscsi_offload_cxn_v2(params, pwrb_handle);
- else
+ if (is_chip_be2_be3r(phba))
beiscsi_offload_cxn_v0(params, pwrb_handle,
phba->init_mem);
+ else
+ beiscsi_offload_cxn_v2(params, pwrb_handle);
be_dws_le_to_cpu(pwrb_handle->pwrb,
sizeof(struct iscsi_target_context_update_wrb));
@@ -4194,6 +4286,7 @@ 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;
@@ -4223,8 +4316,7 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
goto free_hndls;
}
io_task->pwrb_handle = alloc_wrb_handle(phba,
- beiscsi_conn->beiscsi_conn_cid -
- phba->fw_config.iscsi_cid_start);
+ beiscsi_conn->beiscsi_conn_cid);
if (!io_task->pwrb_handle) {
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
@@ -4236,6 +4328,7 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
} 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 *)
@@ -4257,8 +4350,7 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
io_task->psgl_handle;
io_task->pwrb_handle =
alloc_wrb_handle(phba,
- beiscsi_conn->beiscsi_conn_cid -
- phba->fw_config.iscsi_cid_start);
+ beiscsi_conn->beiscsi_conn_cid);
if (!io_task->pwrb_handle) {
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_IO |
@@ -4278,7 +4370,6 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
io_task->pwrb_handle =
beiscsi_conn->plogin_wrb_handle;
}
- beiscsi_conn->task = task;
} else {
spin_lock(&phba->mgmt_sgl_lock);
io_task->psgl_handle = alloc_mgmt_sgl_handle(phba);
@@ -4295,8 +4386,7 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
}
io_task->pwrb_handle =
alloc_wrb_handle(phba,
- beiscsi_conn->beiscsi_conn_cid -
- phba->fw_config.iscsi_cid_start);
+ beiscsi_conn->beiscsi_conn_cid);
if (!io_task->pwrb_handle) {
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
@@ -4324,12 +4414,13 @@ free_io_hndls:
free_mgmt_hndls:
spin_lock(&phba->mgmt_sgl_lock);
free_mgmt_sgl_handle(phba, io_task->psgl_handle);
+ io_task->psgl_handle = NULL;
spin_unlock(&phba->mgmt_sgl_lock);
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;
@@ -4351,7 +4442,6 @@ int beiscsi_iotask_v2(struct iscsi_task *task, struct scatterlist *sg,
unsigned int doorbell = 0;
pwrb = io_task->pwrb_handle->pwrb;
- memset(pwrb, 0, sizeof(*pwrb));
io_task->cmd_bhs->iscsi_hdr.exp_statsn = 0;
io_task->bhs_len = sizeof(struct be_cmd_bhs);
@@ -4465,19 +4555,7 @@ static int beiscsi_mtask(struct iscsi_task *task)
pwrb = io_task->pwrb_handle->pwrb;
memset(pwrb, 0, sizeof(*pwrb));
- if (chip_skh_r(phba->pcidev)) {
- 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->nxt_wrb_index);
- pwrb_typeoffset = SKH_WRB_TYPE_OFFSET;
- } else {
+ 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,
@@ -4489,6 +4567,18 @@ static int beiscsi_mtask(struct iscsi_task *task)
AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, pwrb,
io_task->pwrb_handle->nxt_wrb_index);
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->nxt_wrb_index);
+ pwrb_typeoffset = SKH_WRB_TYPE_OFFSET;
}
@@ -4501,19 +4591,19 @@ static int beiscsi_mtask(struct iscsi_task *task)
case ISCSI_OP_NOOP_OUT:
if (task->hdr->ttt != ISCSI_RESERVED_TAG) {
ADAPTER_SET_WRB_TYPE(pwrb, TGT_DM_CMD, pwrb_typeoffset);
- if (chip_skh_r(phba->pcidev))
- AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ if (is_chip_be2_be3r(phba))
+ AMAP_SET_BITS(struct amap_iscsi_wrb,
dmsg, pwrb, 1);
else
- AMAP_SET_BITS(struct amap_iscsi_wrb,
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
dmsg, pwrb, 1);
} else {
ADAPTER_SET_WRB_TYPE(pwrb, INI_RD_CMD, pwrb_typeoffset);
- if (chip_skh_r(phba->pcidev))
- AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ if (is_chip_be2_be3r(phba))
+ AMAP_SET_BITS(struct amap_iscsi_wrb,
dmsg, pwrb, 0);
else
- AMAP_SET_BITS(struct amap_iscsi_wrb,
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
dmsg, pwrb, 0);
}
hwi_write_buffer(pwrb, task);
@@ -4540,9 +4630,9 @@ static int beiscsi_mtask(struct iscsi_task *task)
}
/* Set the task type */
- io_task->wrb_type = (chip_skh_r(phba->pcidev)) ?
- AMAP_GET_BITS(struct amap_iscsi_wrb_v2, type, pwrb) :
- AMAP_GET_BITS(struct amap_iscsi_wrb, type, pwrb);
+ 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 &
@@ -4834,6 +4924,7 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev,
case OC_SKH_ID1:
phba->generation = BE_GEN4;
phba->iotask_fn = beiscsi_iotask_v2;
+ break;
default:
phba->generation = 0;
}
diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h
index 5946577d79d6..2c06ef3c02ac 100644
--- a/drivers/scsi/be2iscsi/be_main.h
+++ b/drivers/scsi/be2iscsi/be_main.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2012 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -36,7 +36,7 @@
#include "be.h"
#define DRV_NAME "be2iscsi"
-#define BUILD_STR "10.0.272.0"
+#define BUILD_STR "10.0.467.0"
#define BE_NAME "Emulex OneConnect" \
"Open-iSCSI Driver version" BUILD_STR
#define DRV_DESC BE_NAME " " "Driver"
@@ -66,8 +66,9 @@
#define MAX_CPUS 64
#define BEISCSI_MAX_NUM_CPUS 7
-#define OC_SKH_MAX_NUM_CPUS 63
+#define OC_SKH_MAX_NUM_CPUS 31
+#define BEISCSI_VER_STRLEN 32
#define BEISCSI_SGLIST_ELEMENTS 30
@@ -265,7 +266,9 @@ struct invalidate_command_table {
unsigned short cid;
} __packed;
-#define chip_skh_r(pdev) (pdev->device == OC_SKH_ID1)
+#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)))
struct beiscsi_hba {
struct hba_parameters params;
struct hwi_controller *phwi_ctrlr;
@@ -304,10 +307,15 @@ struct beiscsi_hba {
unsigned short avlbl_cids;
unsigned short cid_alloc;
unsigned short cid_free;
- struct beiscsi_conn *conn_table[BE2_MAX_SESSIONS * 2];
struct list_head hba_queue;
+#define BE_MAX_SESSION 2048
+#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];
unsigned short *cid_array;
struct iscsi_endpoint **ep_array;
+ struct beiscsi_conn **conn_table;
struct iscsi_boot_kset *boot_kset;
struct Scsi_Host *shost;
struct iscsi_iface *ipv4_iface;
@@ -339,6 +347,7 @@ struct beiscsi_hba {
struct delayed_work beiscsi_hw_check_task;
u8 mac_address[ETH_ALEN];
+ char fw_ver_str[BEISCSI_VER_STRLEN];
char wq_name[20];
struct workqueue_struct *wq; /* The actuak work queue */
struct be_ctrl_info ctrl;
@@ -563,7 +572,7 @@ struct hwi_async_pdu_context {
* This is a varying size list! Do not add anything
* after this entry!!
*/
- struct hwi_async_entry async_entry[BE2_MAX_SESSIONS * 2];
+ struct hwi_async_entry *async_entry;
};
#define PDUCQE_CODE_MASK 0x0000003F
@@ -749,6 +758,8 @@ 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);
static inline bool beiscsi_error(struct beiscsi_hba *phba)
{
@@ -933,7 +944,7 @@ struct hwi_controller {
struct sgl_handle *psgl_handle_base;
unsigned int wrb_mem_index;
- struct hwi_wrb_context wrb_context[BE2_MAX_SESSIONS * 2];
+ struct hwi_wrb_context *wrb_context;
struct mcc_wrb *pmcc_wrb_base;
struct be_ring default_pdu_hdr;
struct be_ring default_pdu_data;
@@ -970,9 +981,7 @@ struct hwi_context_memory {
struct be_queue_info be_def_hdrq;
struct be_queue_info be_def_dataq;
- struct be_queue_info be_wrbq[BE2_MAX_SESSIONS];
- struct be_mcc_wrb_context *pbe_mcc_context;
-
+ struct be_queue_info *be_wrbq;
struct hwi_async_pdu_context *pasync_ctx;
};
diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c
index 55cc9902263d..245a9595a93a 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.c
+++ b/drivers/scsi/be2iscsi/be_mgmt.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2012 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -368,6 +368,8 @@ int mgmt_check_supported_fw(struct be_ctrl_info *ctrl,
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 mgmt_check_supported_fw\n");
@@ -1260,6 +1262,45 @@ beiscsi_drvr_ver_disp(struct device *dev, struct device_attribute *attr,
}
/**
+ * 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 Scsi_Host *shost = class_to_shost(dev);
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", phba->fw_ver_str);
+}
+
+/**
+ * beiscsi_active_cid_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_cid_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, "%d\n",
+ (phba->params.cxns_per_ctrl - phba->avlbl_cids));
+}
+
+/**
* beiscsi_adap_family_disp()- Display adapter family.
* @dev: ptr to device to get priv structure
* @attr: device attribute, not used.
diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h
index 2e4968add799..04af7e74fe48 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.h
+++ b/drivers/scsi/be2iscsi/be_mgmt.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2012 Emulex
+ * Copyright (C) 2005 - 2013 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -156,25 +156,25 @@ union invalidate_commands_params {
} __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;
@@ -190,7 +190,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;
@@ -207,7 +208,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 {
@@ -311,6 +312,12 @@ int mgmt_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag);
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_cid_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);
diff --git a/drivers/scsi/bnx2fc/bnx2fc.h b/drivers/scsi/bnx2fc/bnx2fc.h
index 11596b2c4702..08b22a901c25 100644
--- a/drivers/scsi/bnx2fc/bnx2fc.h
+++ b/drivers/scsi/bnx2fc/bnx2fc.h
@@ -2,7 +2,7 @@
#define _BNX2FC_H_
/* bnx2fc.h: Broadcom NetXtreme II Linux FCoE offload driver.
*
- * Copyright (c) 2008 - 2011 Broadcom Corporation
+ * Copyright (c) 2008 - 2013 Broadcom 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
@@ -64,10 +64,12 @@
#include "bnx2fc_constants.h"
#define BNX2FC_NAME "bnx2fc"
-#define BNX2FC_VERSION "1.0.13"
+#define BNX2FC_VERSION "1.0.14"
#define PFX "bnx2fc: "
+#define BCM_CHIP_LEN 16
+
#define BNX2X_DOORBELL_PCI_BAR 2
#define BNX2FC_MAX_BD_LEN 0xffff
@@ -241,6 +243,8 @@ struct bnx2fc_hba {
int wait_for_link_down;
int num_ofld_sess;
struct list_head vports;
+
+ char chip_num[BCM_CHIP_LEN];
};
struct bnx2fc_interface {
diff --git a/drivers/scsi/bnx2fc/bnx2fc_els.c b/drivers/scsi/bnx2fc/bnx2fc_els.c
index bdbbb13b8534..b1c9a4f8caee 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_els.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_els.c
@@ -3,7 +3,7 @@
* This file contains helper routines that handle ELS requests
* and responses.
*
- * Copyright (c) 2008 - 2011 Broadcom Corporation
+ * Copyright (c) 2008 - 2013 Broadcom 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
diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
index 7dffec1e5715..69ac55495c1d 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
@@ -3,7 +3,7 @@
* cnic modules to create FCoE instances, send/receive non-offloaded
* FIP/FCoE packets, listen to link events etc.
*
- * Copyright (c) 2008 - 2011 Broadcom Corporation
+ * Copyright (c) 2008 - 2013 Broadcom 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
@@ -22,7 +22,7 @@ DEFINE_PER_CPU(struct bnx2fc_percpu_s, bnx2fc_percpu);
#define DRV_MODULE_NAME "bnx2fc"
#define DRV_MODULE_VERSION BNX2FC_VERSION
-#define DRV_MODULE_RELDATE "Dec 21, 2012"
+#define DRV_MODULE_RELDATE "Mar 08, 2013"
static char version[] =
@@ -679,6 +679,7 @@ static int bnx2fc_shost_config(struct fc_lport *lport, struct device *dev)
{
struct fcoe_port *port = lport_priv(lport);
struct bnx2fc_interface *interface = port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
struct Scsi_Host *shost = lport->host;
int rc = 0;
@@ -699,8 +700,9 @@ 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,
+ snprintf(fc_host_symbolic_name(lport->host), 256,
+ "%s (Broadcom %s) v%s over %s",
+ BNX2FC_NAME, hba->chip_num, BNX2FC_VERSION,
interface->netdev->name);
return 0;
@@ -1656,23 +1658,60 @@ 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:
+ strncpy(hba->chip_num, "BCM57710", BCM_CHIP_LEN);
+ break;
+ case PCI_DEVICE_ID_NX2_57711:
+ strncpy(hba->chip_num, "BCM57711", BCM_CHIP_LEN);
+ break;
+ case PCI_DEVICE_ID_NX2_57712:
+ case PCI_DEVICE_ID_NX2_57712_MF:
+ case PCI_DEVICE_ID_NX2_57712_VF:
+ strncpy(hba->chip_num, "BCM57712", BCM_CHIP_LEN);
+ break;
+ case PCI_DEVICE_ID_NX2_57800:
+ case PCI_DEVICE_ID_NX2_57800_MF:
+ case PCI_DEVICE_ID_NX2_57800_VF:
+ strncpy(hba->chip_num, "BCM57800", BCM_CHIP_LEN);
+ break;
+ case PCI_DEVICE_ID_NX2_57810:
+ case PCI_DEVICE_ID_NX2_57810_MF:
+ case PCI_DEVICE_ID_NX2_57810_VF:
+ strncpy(hba->chip_num, "BCM57810", BCM_CHIP_LEN);
+ 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:
+ strncpy(hba->chip_num, "BCM57840", BCM_CHIP_LEN);
+ 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;
}
diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c
index 50510ffe1bf5..c0d035a8f8f9 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_hwi.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c
@@ -2,7 +2,7 @@
* This file contains the code that low level functions that interact
* with 57712 FCoE firmware.
*
- * Copyright (c) 2008 - 2011 Broadcom Corporation
+ * Copyright (c) 2008 - 2013 Broadcom 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
@@ -126,7 +126,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;
- fcoe_init3.perf_config = 1;
+ /*
+ * 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;
diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c
index 723a9a8ba5ee..575142e92d9c 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_io.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_io.c
@@ -1,7 +1,7 @@
/* bnx2fc_io.c: Broadcom NetXtreme II Linux FCoE offload driver.
* IO manager and SCSI IO processing.
*
- * Copyright (c) 2008 - 2011 Broadcom Corporation
+ * Copyright (c) 2008 - 2013 Broadcom 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
@@ -1270,8 +1270,11 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
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))) {
+ 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);
diff --git a/drivers/scsi/bnx2fc/bnx2fc_tgt.c b/drivers/scsi/bnx2fc/bnx2fc_tgt.c
index c57a3bb8a9fb..4d93177dfb53 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_tgt.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_tgt.c
@@ -2,7 +2,7 @@
* Handles operations such as session offload/upload etc, and manages
* session resources such as connection id and qp resources.
*
- * Copyright (c) 2008 - 2011 Broadcom Corporation
+ * Copyright (c) 2008 - 2013 Broadcom 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
diff --git a/drivers/scsi/csiostor/csio_lnode.h b/drivers/scsi/csiostor/csio_lnode.h
index 0f9c04175b11..372a67d122d3 100644
--- a/drivers/scsi/csiostor/csio_lnode.h
+++ b/drivers/scsi/csiostor/csio_lnode.h
@@ -114,7 +114,7 @@ struct csio_lnode_stats {
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]; /* fw events */
+ 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 */
diff --git a/drivers/scsi/csiostor/csio_rnode.h b/drivers/scsi/csiostor/csio_rnode.h
index 65940096a80d..433434221222 100644
--- a/drivers/scsi/csiostor/csio_rnode.h
+++ b/drivers/scsi/csiostor/csio_rnode.h
@@ -63,7 +63,7 @@ struct csio_rnode_stats {
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]; /* fw events */
+ 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
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index 98436c363035..b6d1f92ed33c 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -38,7 +38,7 @@
#define DRV_NAME "fnic"
#define DRV_DESCRIPTION "Cisco FCoE HBA Driver"
-#define DRV_VERSION "1.5.0.2"
+#define DRV_VERSION "1.5.0.22"
#define PFX DRV_NAME ": "
#define DFX DRV_NAME "%d: "
@@ -192,6 +192,18 @@ enum fnic_state {
struct mempool;
+enum fnic_evt {
+ FNIC_EVT_START_VLAN_DISC = 1,
+ FNIC_EVT_START_FCF_DISC = 2,
+ FNIC_EVT_MAX,
+};
+
+struct fnic_event {
+ struct list_head list;
+ struct fnic *fnic;
+ enum fnic_evt event;
+};
+
/* Per-instance private data structure */
struct fnic {
struct fc_lport *lport;
@@ -254,6 +266,18 @@ struct fnic {
struct sk_buff_head frame_queue;
struct sk_buff_head tx_queue;
+ /*** FIP related data members -- start ***/
+ void (*set_vlan)(struct fnic *, u16 vlan);
+ struct work_struct fip_frame_work;
+ struct sk_buff_head fip_frame_queue;
+ struct timer_list fip_timer;
+ struct list_head vlans;
+ spinlock_t vlans_lock;
+
+ struct work_struct event_work;
+ struct list_head evlist;
+ /*** FIP related data members -- end ***/
+
/* copy work queue cache line section */
____cacheline_aligned struct vnic_wq_copy wq_copy[FNIC_WQ_COPY_MAX];
/* completion queue cache line section */
@@ -278,6 +302,7 @@ static inline struct fnic *fnic_from_ctlr(struct fcoe_ctlr *fip)
}
extern struct workqueue_struct *fnic_event_queue;
+extern struct workqueue_struct *fnic_fip_queue;
extern struct device_attribute *fnic_attrs[];
void fnic_clear_intr_mode(struct fnic *fnic);
@@ -289,6 +314,7 @@ 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_handle_link(struct work_struct *work);
+void fnic_handle_event(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);
@@ -321,6 +347,12 @@ void fnic_handle_link_event(struct fnic *fnic);
int fnic_is_abts_pending(struct fnic *, struct scsi_cmnd *);
+void fnic_handle_fip_frame(struct work_struct *work);
+void fnic_handle_fip_event(struct fnic *fnic);
+void fnic_fcoe_reset_vlans(struct fnic *fnic);
+void fnic_fcoe_evlist_free(struct fnic *fnic);
+extern void fnic_handle_fip_timer(struct fnic *fnic);
+
static inline int
fnic_chk_state_flags_locked(struct fnic *fnic, unsigned long st_flags)
{
diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c
index 483eb9dbe663..006fa92a02df 100644
--- a/drivers/scsi/fnic/fnic_fcs.c
+++ b/drivers/scsi/fnic/fnic_fcs.c
@@ -31,12 +31,20 @@
#include <scsi/libfc.h>
#include "fnic_io.h"
#include "fnic.h"
+#include "fnic_fip.h"
#include "cq_enet_desc.h"
#include "cq_exch_desc.h"
+static u8 fcoe_all_fcfs[ETH_ALEN];
+struct workqueue_struct *fnic_fip_queue;
struct workqueue_struct *fnic_event_queue;
static void fnic_set_eth_mode(struct fnic *);
+static void fnic_fcoe_send_vlan_req(struct fnic *fnic);
+static void fnic_fcoe_start_fcf_disc(struct fnic *fnic);
+static void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct sk_buff *);
+static int fnic_fcoe_vlan_check(struct fnic *fnic, u16 flag);
+static int fnic_fcoe_handle_fip_frame(struct fnic *fnic, struct sk_buff *skb);
void fnic_handle_link(struct work_struct *work)
{
@@ -69,6 +77,11 @@ void fnic_handle_link(struct work_struct *work)
FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
"link down\n");
fcoe_ctlr_link_down(&fnic->ctlr);
+ if (fnic->config.flags & VFCF_FIP_CAPABLE) {
+ /* start FCoE VLAN discovery */
+ fnic_fcoe_send_vlan_req(fnic);
+ return;
+ }
FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
"link up\n");
fcoe_ctlr_link_up(&fnic->ctlr);
@@ -79,6 +92,11 @@ void fnic_handle_link(struct work_struct *work)
} else if (fnic->link_status) {
/* DOWN -> UP */
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ if (fnic->config.flags & VFCF_FIP_CAPABLE) {
+ /* start FCoE VLAN discovery */
+ fnic_fcoe_send_vlan_req(fnic);
+ return;
+ }
FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link up\n");
fcoe_ctlr_link_up(&fnic->ctlr);
} else {
@@ -128,6 +146,441 @@ void fnic_handle_frame(struct work_struct *work)
}
}
+void fnic_fcoe_evlist_free(struct fnic *fnic)
+{
+ struct fnic_event *fevt = NULL;
+ struct fnic_event *next = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (list_empty(&fnic->evlist)) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+
+ list_for_each_entry_safe(fevt, next, &fnic->evlist, list) {
+ list_del(&fevt->list);
+ kfree(fevt);
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
+void fnic_handle_event(struct work_struct *work)
+{
+ struct fnic *fnic = container_of(work, struct fnic, event_work);
+ struct fnic_event *fevt = NULL;
+ struct fnic_event *next = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (list_empty(&fnic->evlist)) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+
+ list_for_each_entry_safe(fevt, next, &fnic->evlist, list) {
+ if (fnic->stop_rx_link_events) {
+ list_del(&fevt->list);
+ kfree(fevt);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+ /*
+ * 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) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+
+ list_del(&fevt->list);
+ switch (fevt->event) {
+ case FNIC_EVT_START_VLAN_DISC:
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ fnic_fcoe_send_vlan_req(fnic);
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ break;
+ case FNIC_EVT_START_FCF_DISC:
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ "Start FCF Discovery\n");
+ fnic_fcoe_start_fcf_disc(fnic);
+ break;
+ default:
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ "Unknown event 0x%x\n", fevt->event);
+ break;
+ }
+ kfree(fevt);
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
+/**
+ * Check if the Received FIP FLOGI frame is rejected
+ * @fip: The FCoE controller that received the frame
+ * @skb: The received FIP frame
+ *
+ * Returns non-zero if the frame is rejected with unsupported cmd with
+ * insufficient resource els explanation.
+ */
+static inline int is_fnic_fip_flogi_reject(struct fcoe_ctlr *fip,
+ struct sk_buff *skb)
+{
+ struct fc_lport *lport = fip->lp;
+ struct fip_header *fiph;
+ struct fc_frame_header *fh = NULL;
+ struct fip_desc *desc;
+ struct fip_encaps *els;
+ enum fip_desc_type els_dtype = 0;
+ u16 op;
+ u8 els_op;
+ u8 sub;
+
+ size_t els_len = 0;
+ size_t rlen;
+ size_t dlen = 0;
+
+ if (skb_linearize(skb))
+ return 0;
+
+ if (skb->len < sizeof(*fiph))
+ return 0;
+
+ fiph = (struct fip_header *)skb->data;
+ op = ntohs(fiph->fip_op);
+ sub = fiph->fip_subcode;
+
+ if (op != FIP_OP_LS)
+ return 0;
+
+ if (sub != FIP_SC_REP)
+ return 0;
+
+ rlen = ntohs(fiph->fip_dl_len) * 4;
+ if (rlen + sizeof(*fiph) > skb->len)
+ return 0;
+
+ desc = (struct fip_desc *)(fiph + 1);
+ dlen = desc->fip_dlen * FIP_BPW;
+
+ if (desc->fip_dtype == FIP_DT_FLOGI) {
+
+ shost_printk(KERN_DEBUG, lport->host,
+ " FIP TYPE FLOGI: fab name:%llx "
+ "vfid:%d map:%x\n",
+ fip->sel_fcf->fabric_name, fip->sel_fcf->vfid,
+ fip->sel_fcf->fc_map);
+ if (dlen < sizeof(*els) + sizeof(*fh) + 1)
+ return 0;
+
+ els_len = dlen - sizeof(*els);
+ els = (struct fip_encaps *)desc;
+ fh = (struct fc_frame_header *)(els + 1);
+ els_dtype = desc->fip_dtype;
+
+ if (!fh)
+ return 0;
+
+ /*
+ * ELS command code, reason and explanation should be = Reject,
+ * unsupported command and insufficient resource
+ */
+ els_op = *(u8 *)(fh + 1);
+ if (els_op == ELS_LS_RJT) {
+ shost_printk(KERN_INFO, lport->host,
+ "Flogi Request Rejected by Switch\n");
+ return 1;
+ }
+ shost_printk(KERN_INFO, lport->host,
+ "Flogi Request Accepted by Switch\n");
+ }
+ return 0;
+}
+
+static void fnic_fcoe_send_vlan_req(struct fnic *fnic)
+{
+ struct fcoe_ctlr *fip = &fnic->ctlr;
+ struct sk_buff *skb;
+ char *eth_fr;
+ int fr_len;
+ struct fip_vlan *vlan;
+ u64 vlan_tov;
+
+ fnic_fcoe_reset_vlans(fnic);
+ fnic->set_vlan(fnic, 0);
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host,
+ "Sending VLAN request...\n");
+ skb = dev_alloc_skb(sizeof(struct fip_vlan));
+ if (!skb)
+ return;
+
+ fr_len = sizeof(*vlan);
+ eth_fr = (char *)skb->data;
+ vlan = (struct fip_vlan *)eth_fr;
+
+ memset(vlan, 0, sizeof(*vlan));
+ memcpy(vlan->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
+ memcpy(vlan->eth.h_dest, fcoe_all_fcfs, ETH_ALEN);
+ 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;
+ memcpy(&vlan->desc.mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
+
+ 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(fip->lp->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);
+ fip->send(fip, skb);
+
+ /* set a timer so that we can retry if there no response */
+ vlan_tov = jiffies + msecs_to_jiffies(FCOE_CTLR_FIPVLAN_TOV);
+ mod_timer(&fnic->fip_timer, round_jiffies(vlan_tov));
+}
+
+static void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct sk_buff *skb)
+{
+ struct fcoe_ctlr *fip = &fnic->ctlr;
+ struct fip_header *fiph;
+ struct fip_desc *desc;
+ u16 vid;
+ size_t rlen;
+ size_t dlen;
+ struct fcoe_vlan *vlan;
+ u64 sol_time;
+ unsigned long flags;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host,
+ "Received VLAN response...\n");
+
+ fiph = (struct fip_header *) skb->data;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host,
+ "Received VLAN response... OP 0x%x SUB_OP 0x%x\n",
+ ntohs(fiph->fip_op), fiph->fip_subcode);
+
+ rlen = ntohs(fiph->fip_dl_len) * 4;
+ fnic_fcoe_reset_vlans(fnic);
+ spin_lock_irqsave(&fnic->vlans_lock, flags);
+ 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);
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "process_vlan_resp: FIP VLAN %d\n", vid);
+ vlan = kmalloc(sizeof(*vlan),
+ GFP_ATOMIC);
+ if (!vlan) {
+ /* retry from timer */
+ spin_unlock_irqrestore(&fnic->vlans_lock,
+ flags);
+ goto out;
+ }
+ memset(vlan, 0, sizeof(struct fcoe_vlan));
+ vlan->vid = vid & 0x0fff;
+ vlan->state = FIP_VLAN_AVAIL;
+ list_add_tail(&vlan->list, &fnic->vlans);
+ break;
+ }
+ desc = (struct fip_desc *)((char *)desc + dlen);
+ rlen -= dlen;
+ }
+
+ /* any VLAN descriptors present ? */
+ if (list_empty(&fnic->vlans)) {
+ /* retry from timer */
+ FNIC_FCS_DBG(KERN_INFO, fnic->lport->host,
+ "No VLAN descriptors in FIP VLAN response\n");
+ spin_unlock_irqrestore(&fnic->vlans_lock, flags);
+ goto out;
+ }
+
+ vlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, list);
+ fnic->set_vlan(fnic, vlan->vid);
+ vlan->state = FIP_VLAN_SENT; /* sent now */
+ vlan->sol_count++;
+ spin_unlock_irqrestore(&fnic->vlans_lock, flags);
+
+ /* start the solicitation */
+ fcoe_ctlr_link_up(fip);
+
+ sol_time = jiffies + msecs_to_jiffies(FCOE_CTLR_START_DELAY);
+ mod_timer(&fnic->fip_timer, round_jiffies(sol_time));
+out:
+ return;
+}
+
+static void fnic_fcoe_start_fcf_disc(struct fnic *fnic)
+{
+ unsigned long flags;
+ struct fcoe_vlan *vlan;
+ u64 sol_time;
+
+ spin_lock_irqsave(&fnic->vlans_lock, flags);
+ vlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, list);
+ fnic->set_vlan(fnic, vlan->vid);
+ vlan->state = FIP_VLAN_SENT; /* sent now */
+ vlan->sol_count = 1;
+ spin_unlock_irqrestore(&fnic->vlans_lock, flags);
+
+ /* start the solicitation */
+ fcoe_ctlr_link_up(&fnic->ctlr);
+
+ sol_time = jiffies + msecs_to_jiffies(FCOE_CTLR_START_DELAY);
+ mod_timer(&fnic->fip_timer, round_jiffies(sol_time));
+}
+
+static int fnic_fcoe_vlan_check(struct fnic *fnic, u16 flag)
+{
+ unsigned long flags;
+ struct fcoe_vlan *fvlan;
+
+ spin_lock_irqsave(&fnic->vlans_lock, flags);
+ if (list_empty(&fnic->vlans)) {
+ spin_unlock_irqrestore(&fnic->vlans_lock, flags);
+ return -EINVAL;
+ }
+
+ fvlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, list);
+ if (fvlan->state == FIP_VLAN_USED) {
+ spin_unlock_irqrestore(&fnic->vlans_lock, flags);
+ return 0;
+ }
+
+ if (fvlan->state == FIP_VLAN_SENT) {
+ fvlan->state = FIP_VLAN_USED;
+ spin_unlock_irqrestore(&fnic->vlans_lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&fnic->vlans_lock, flags);
+ return -EINVAL;
+}
+
+static void fnic_event_enq(struct fnic *fnic, enum fnic_evt ev)
+{
+ struct fnic_event *fevt;
+ unsigned long flags;
+
+ fevt = kmalloc(sizeof(*fevt), GFP_ATOMIC);
+ if (!fevt)
+ return;
+
+ fevt->fnic = fnic;
+ fevt->event = ev;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ list_add_tail(&fevt->list, &fnic->evlist);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ schedule_work(&fnic->event_work);
+}
+
+static int fnic_fcoe_handle_fip_frame(struct fnic *fnic, struct sk_buff *skb)
+{
+ struct fip_header *fiph;
+ int ret = 1;
+ u16 op;
+ u8 sub;
+
+ if (!skb || !(skb->data))
+ return -1;
+
+ if (skb_linearize(skb))
+ goto drop;
+
+ fiph = (struct fip_header *)skb->data;
+ op = ntohs(fiph->fip_op);
+ sub = fiph->fip_subcode;
+
+ if (FIP_VER_DECAPS(fiph->fip_ver) != FIP_VER)
+ goto drop;
+
+ if (ntohs(fiph->fip_dl_len) * FIP_BPW + sizeof(*fiph) > skb->len)
+ goto drop;
+
+ if (op == FIP_OP_DISC && sub == FIP_SC_ADV) {
+ if (fnic_fcoe_vlan_check(fnic, ntohs(fiph->fip_flags)))
+ goto drop;
+ /* pass it on to fcoe */
+ ret = 1;
+ } else if (op == FIP_OP_VLAN && sub == FIP_SC_VL_REP) {
+ /* set the vlan as used */
+ fnic_fcoe_process_vlan_resp(fnic, skb);
+ ret = 0;
+ } else if (op == FIP_OP_CTRL && sub == FIP_SC_CLR_VLINK) {
+ /* received CVL request, restart vlan disc */
+ fnic_event_enq(fnic, FNIC_EVT_START_VLAN_DISC);
+ /* pass it on to fcoe */
+ ret = 1;
+ }
+drop:
+ return ret;
+}
+
+void fnic_handle_fip_frame(struct work_struct *work)
+{
+ struct fnic *fnic = container_of(work, struct fnic, fip_frame_work);
+ unsigned long flags;
+ struct sk_buff *skb;
+ struct ethhdr *eh;
+
+ while ((skb = skb_dequeue(&fnic->fip_frame_queue))) {
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (fnic->stop_rx_link_events) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ dev_kfree_skb(skb);
+ return;
+ }
+ /*
+ * 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->fip_frame_queue, skb);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ eh = (struct ethhdr *)skb->data;
+ if (eh->h_proto == htons(ETH_P_FIP)) {
+ skb_pull(skb, sizeof(*eh));
+ if (fnic_fcoe_handle_fip_frame(fnic, skb) <= 0) {
+ dev_kfree_skb(skb);
+ continue;
+ }
+ /*
+ * If there's FLOGI rejects - clear all
+ * fcf's & restart from scratch
+ */
+ if (is_fnic_fip_flogi_reject(&fnic->ctlr, skb)) {
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "Trigger a Link down - VLAN Disc\n");
+ fcoe_ctlr_link_down(&fnic->ctlr);
+ /* start FCoE VLAN discovery */
+ fnic_fcoe_send_vlan_req(fnic);
+ dev_kfree_skb(skb);
+ continue;
+ }
+ fcoe_ctlr_recv(&fnic->ctlr, skb);
+ continue;
+ }
+ }
+}
+
/**
* fnic_import_rq_eth_pkt() - handle received FCoE or FIP frame.
* @fnic: fnic instance.
@@ -150,8 +603,14 @@ static inline int fnic_import_rq_eth_pkt(struct fnic *fnic, struct sk_buff *skb)
skb_reset_mac_header(skb);
}
if (eh->h_proto == htons(ETH_P_FIP)) {
- skb_pull(skb, sizeof(*eh));
- fcoe_ctlr_recv(&fnic->ctlr, skb);
+ if (!(fnic->config.flags & VFCF_FIP_CAPABLE)) {
+ printk(KERN_ERR "Dropped FIP frame, as firmware "
+ "uses non-FIP mode, Enable FIP "
+ "using UCSM\n");
+ goto drop;
+ }
+ skb_queue_tail(&fnic->fip_frame_queue, skb);
+ queue_work(fnic_fip_queue, &fnic->fip_frame_work);
return 1; /* let caller know packet was used */
}
if (eh->h_proto != htons(ETH_P_FCOE))
@@ -720,3 +1179,104 @@ void fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf)
dev_kfree_skb(fp_skb(fp));
buf->os_buf = NULL;
}
+
+void fnic_fcoe_reset_vlans(struct fnic *fnic)
+{
+ unsigned long flags;
+ struct fcoe_vlan *vlan;
+ struct fcoe_vlan *next;
+
+ /*
+ * indicate a link down to fcoe so that all fcf's are free'd
+ * might not be required since we did this before sending vlan
+ * discovery request
+ */
+ spin_lock_irqsave(&fnic->vlans_lock, flags);
+ if (!list_empty(&fnic->vlans)) {
+ list_for_each_entry_safe(vlan, next, &fnic->vlans, list) {
+ list_del(&vlan->list);
+ kfree(vlan);
+ }
+ }
+ spin_unlock_irqrestore(&fnic->vlans_lock, flags);
+}
+
+void fnic_handle_fip_timer(struct fnic *fnic)
+{
+ unsigned long flags;
+ struct fcoe_vlan *vlan;
+ u64 sol_time;
+
+ 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 (fnic->ctlr.mode == FIP_ST_NON_FIP)
+ return;
+
+ spin_lock_irqsave(&fnic->vlans_lock, flags);
+ if (list_empty(&fnic->vlans)) {
+ /* no vlans available, try again */
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ "Start VLAN Discovery\n");
+ spin_unlock_irqrestore(&fnic->vlans_lock, flags);
+ fnic_event_enq(fnic, FNIC_EVT_START_VLAN_DISC);
+ return;
+ }
+
+ vlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, list);
+ shost_printk(KERN_DEBUG, fnic->lport->host,
+ "fip_timer: vlan %d state %d sol_count %d\n",
+ vlan->vid, vlan->state, vlan->sol_count);
+ switch (vlan->state) {
+ case FIP_VLAN_USED:
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ "FIP VLAN is selected for FC transaction\n");
+ spin_unlock_irqrestore(&fnic->vlans_lock, flags);
+ break;
+ case FIP_VLAN_FAILED:
+ /* if all vlans are in failed state, restart vlan disc */
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ "Start VLAN Discovery\n");
+ spin_unlock_irqrestore(&fnic->vlans_lock, flags);
+ fnic_event_enq(fnic, FNIC_EVT_START_VLAN_DISC);
+ break;
+ case FIP_VLAN_SENT:
+ if (vlan->sol_count >= FCOE_CTLR_MAX_SOL) {
+ /*
+ * no response on this vlan, remove from the list.
+ * Try the next vlan
+ */
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "Dequeue this VLAN ID %d from list\n",
+ vlan->vid);
+ list_del(&vlan->list);
+ kfree(vlan);
+ vlan = NULL;
+ if (list_empty(&fnic->vlans)) {
+ /* we exhausted all vlans, restart vlan disc */
+ spin_unlock_irqrestore(&fnic->vlans_lock,
+ flags);
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "fip_timer: vlan list empty, "
+ "trigger vlan disc\n");
+ fnic_event_enq(fnic, FNIC_EVT_START_VLAN_DISC);
+ return;
+ }
+ /* check the next vlan */
+ vlan = list_first_entry(&fnic->vlans, struct fcoe_vlan,
+ list);
+ fnic->set_vlan(fnic, vlan->vid);
+ vlan->state = FIP_VLAN_SENT; /* sent now */
+ }
+ spin_unlock_irqrestore(&fnic->vlans_lock, flags);
+ vlan->sol_count++;
+ sol_time = jiffies + msecs_to_jiffies
+ (FCOE_CTLR_START_DELAY);
+ mod_timer(&fnic->fip_timer, round_jiffies(sol_time));
+ break;
+ }
+}
diff --git a/drivers/scsi/fnic/fnic_fip.h b/drivers/scsi/fnic/fnic_fip.h
new file mode 100644
index 000000000000..87e74c2ab971
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_fip.h
@@ -0,0 +1,68 @@
+/*
+ * 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_FIP_H_
+#define _FNIC_FIP_H_
+
+
+#define FCOE_CTLR_START_DELAY 2000 /* ms after first adv. to choose FCF */
+#define FCOE_CTLR_FIPVLAN_TOV 2000 /* ms after FIP VLAN disc */
+#define FCOE_CTLR_MAX_SOL 8
+
+#define FINC_MAX_FLOGI_REJECTS 8
+
+/*
+ * FIP_DT_VLAN descriptor.
+ */
+struct fip_vlan_desc {
+ struct fip_desc fd_desc;
+ __be16 fd_vlan;
+} __attribute__((packed));
+
+struct vlan {
+ __be16 vid;
+ __be16 type;
+};
+
+/*
+ * VLAN entry.
+ */
+struct fcoe_vlan {
+ struct list_head list;
+ u16 vid; /* vlan ID */
+ u16 sol_count; /* no. of sols sent */
+ u16 state; /* state */
+};
+
+enum fip_vlan_state {
+ FIP_VLAN_AVAIL = 0, /* don't do anything */
+ FIP_VLAN_SENT = 1, /* sent */
+ FIP_VLAN_USED = 2, /* succeed */
+ FIP_VLAN_FAILED = 3, /* failed to response */
+};
+
+struct fip_vlan {
+ struct ethhdr eth;
+ struct fip_header fip;
+ struct {
+ struct fip_mac_desc mac;
+ struct fip_wwn_desc wwnn;
+ } desc;
+};
+
+#endif /* __FINC_FIP_H_ */
diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c
index d601ac543c52..5f09d1814d26 100644
--- a/drivers/scsi/fnic/fnic_main.c
+++ b/drivers/scsi/fnic/fnic_main.c
@@ -39,6 +39,7 @@
#include "vnic_intr.h"
#include "vnic_stats.h"
#include "fnic_io.h"
+#include "fnic_fip.h"
#include "fnic.h"
#define PCI_DEVICE_ID_CISCO_FNIC 0x0045
@@ -292,6 +293,13 @@ static void fnic_notify_timer(unsigned long data)
round_jiffies(jiffies + FNIC_NOTIFY_TIMER_PERIOD));
}
+static void fnic_fip_notify_timer(unsigned long data)
+{
+ struct fnic *fnic = (struct fnic *)data;
+
+ fnic_handle_fip_timer(fnic);
+}
+
static void fnic_notify_timer_start(struct fnic *fnic)
{
switch (vnic_dev_get_intr_mode(fnic->vdev)) {
@@ -403,6 +411,12 @@ static u8 *fnic_get_mac(struct fc_lport *lport)
return fnic->data_src_addr;
}
+static void fnic_set_vlan(struct fnic *fnic, u16 vlan_id)
+{
+ u16 old_vlan;
+ old_vlan = vnic_dev_set_default_vlan(fnic->vdev, vlan_id);
+}
+
static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct Scsi_Host *host;
@@ -620,7 +634,29 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
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);
+ fnic->set_vlan = fnic_set_vlan;
fcoe_ctlr_init(&fnic->ctlr, FIP_MODE_AUTO);
+ setup_timer(&fnic->fip_timer, fnic_fip_notify_timer,
+ (unsigned long)fnic);
+ spin_lock_init(&fnic->vlans_lock);
+ INIT_WORK(&fnic->fip_frame_work, fnic_handle_fip_frame);
+ INIT_WORK(&fnic->event_work, fnic_handle_event);
+ skb_queue_head_init(&fnic->fip_frame_queue);
+ spin_lock_irqsave(&fnic_list_lock, flags);
+ if (!fnic_fip_queue) {
+ fnic_fip_queue =
+ create_singlethread_workqueue("fnic_fip_q");
+ if (!fnic_fip_queue) {
+ spin_unlock_irqrestore(&fnic_list_lock, flags);
+ printk(KERN_ERR PFX "fnic FIP work queue "
+ "create failed\n");
+ err = -ENOMEM;
+ goto err_out_free_max_pool;
+ }
+ }
+ spin_unlock_irqrestore(&fnic_list_lock, flags);
+ INIT_LIST_HEAD(&fnic->evlist);
+ INIT_LIST_HEAD(&fnic->vlans);
} else {
shost_printk(KERN_INFO, fnic->lport->host,
"firmware uses non-FIP mode\n");
@@ -807,6 +843,13 @@ static void fnic_remove(struct pci_dev *pdev)
skb_queue_purge(&fnic->frame_queue);
skb_queue_purge(&fnic->tx_queue);
+ if (fnic->config.flags & VFCF_FIP_CAPABLE) {
+ del_timer_sync(&fnic->fip_timer);
+ skb_queue_purge(&fnic->fip_frame_queue);
+ fnic_fcoe_reset_vlans(fnic);
+ fnic_fcoe_evlist_free(fnic);
+ }
+
/*
* Log off the fabric. This stops all remote ports, dns port,
* logs off the fabric. This flushes all rport, disc, lport work
@@ -889,8 +932,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,
- 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;
@@ -951,6 +994,10 @@ static void __exit fnic_cleanup_module(void)
{
pci_unregister_driver(&fnic_driver);
destroy_workqueue(fnic_event_queue);
+ if (fnic_fip_queue) {
+ flush_workqueue(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);
diff --git a/drivers/scsi/fnic/vnic_dev.c b/drivers/scsi/fnic/vnic_dev.c
index b576be734e2e..9795d6f3e197 100644
--- a/drivers/scsi/fnic/vnic_dev.c
+++ b/drivers/scsi/fnic/vnic_dev.c
@@ -584,6 +584,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)
diff --git a/drivers/scsi/fnic/vnic_dev.h b/drivers/scsi/fnic/vnic_dev.h
index f9935a8a5a09..40d4195f562b 100644
--- a/drivers/scsi/fnic/vnic_dev.h
+++ b/drivers/scsi/fnic/vnic_dev.h
@@ -148,6 +148,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..3e2fcbda6aed 100644
--- a/drivers/scsi/fnic/vnic_devcmd.h
+++ b/drivers/scsi/fnic/vnic_devcmd.h
@@ -196,6 +196,73 @@ 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)
};
/* flags for CMD_OPEN */
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index cc82d0f322b6..4e31caa21ddf 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -2179,7 +2179,7 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
return 0;
}
- if (vhost->state == IBMVFC_ACTIVE) {
+ if (vhost->logged_in) {
evt = ibmvfc_get_event(vhost);
ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
@@ -2190,7 +2190,12 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
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);
+ if (!(vhost->login_buf->resp.capabilities & IBMVFC_CAN_SUPPRESS_ABTS))
+ type &= ~IBMVFC_TMF_SUPPRESS_ABTS;
+ if (vhost->state == IBMVFC_ACTIVE)
+ tmf->flags = (type | IBMVFC_TMF_LUA_VALID);
+ else
+ tmf->flags = ((type & IBMVFC_TMF_SUPPRESS_ABTS) | IBMVFC_TMF_LUA_VALID);
tmf->cancel_key = (unsigned long)sdev->hostdata;
tmf->my_cancel_key = (unsigned long)starget->hostdata;
@@ -2327,7 +2332,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)
@@ -2383,24 +2388,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;
}
@@ -2410,29 +2421,47 @@ 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_noreset - Device iterated cancel all function
+ * @sdev: scsi device struct
+ * @data: return code
+ *
+ **/
+static void ibmvfc_dev_cancel_all_noreset(struct scsi_device *sdev, void *data)
+{
+ unsigned long *rc = data;
+ *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS);
+}
+
+/**
* ibmvfc_dev_cancel_all_reset - Device iterated cancel all function
* @sdev: scsi device struct
* @data: return code
@@ -2449,26 +2478,33 @@ 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;
+ int block_rc;
+ int reset_rc = 0;
int rc = FAILED;
unsigned long cancel_rc = 0;
ENTER;
- fc_block_scsi_eh(cmd);
+ block_rc = fc_block_scsi_eh(cmd);
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) {
+ starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_reset);
+ reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target");
+ } 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;
}
@@ -2480,12 +2516,16 @@ static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd)
**/
static int ibmvfc_eh_host_reset_handler(struct scsi_cmnd *cmd)
{
- int rc;
+ int rc, block_rc;
struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
- fc_block_scsi_eh(cmd);
+ block_rc = fc_block_scsi_eh(cmd);
dev_err(vhost->dev, "Resetting connection due to error recovery\n");
rc = ibmvfc_issue_fc_host_lip(vhost->host);
+
+ if (block_rc == FAST_IO_FAIL)
+ return FAST_IO_FAIL;
+
return rc ? FAILED : SUCCESS;
}
@@ -2509,8 +2549,7 @@ static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
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);
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index 3be8af624e6f..017a5290e8c1 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -29,8 +29,8 @@
#include "viosrp.h"
#define IBMVFC_NAME "ibmvfc"
-#define IBMVFC_DRIVER_VERSION "1.0.10"
-#define IBMVFC_DRIVER_DATE "(August 24, 2012)"
+#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
@@ -208,10 +208,10 @@ struct ibmvfc_npiv_login_resp {
u16 error;
u32 flags;
#define IBMVFC_NATIVE_FC 0x01
-#define IBMVFC_CAN_FLUSH_ON_HALT 0x08
u32 reserved;
u64 capabilities;
#define IBMVFC_CAN_FLUSH_ON_HALT 0x08
+#define IBMVFC_CAN_SUPPRESS_ABTS 0x10
u32 max_cmds;
u32 scsi_id_sz;
u64 max_dma_len;
@@ -351,6 +351,7 @@ struct ibmvfc_tmf {
#define IBMVFC_TMF_LUN_RESET 0x10
#define IBMVFC_TMF_TGT_RESET 0x20
#define IBMVFC_TMF_LUA_VALID 0x40
+#define IBMVFC_TMF_SUPPRESS_ABTS 0x80
u32 cancel_key;
u32 my_cancel_key;
u32 pad;
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index 2197b57fb225..82a3c1ec8706 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -4777,7 +4777,7 @@ static int ipr_eh_host_reset(struct scsi_cmnd *cmd)
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) {
+ 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");
@@ -6421,7 +6421,7 @@ static void ipr_build_ata_ioadl64(struct ipr_cmnd *ipr_cmd,
{
u32 ioadl_flags = 0;
struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
- struct ipr_ioadl64_desc *ioadl64 = ipr_cmd->i.ioadl64;
+ struct ipr_ioadl64_desc *ioadl64 = ipr_cmd->i.ata_ioadl.ioadl64;
struct ipr_ioadl64_desc *last_ioadl64 = NULL;
int len = qc->nbytes;
struct scatterlist *sg;
@@ -6441,7 +6441,7 @@ static void ipr_build_ata_ioadl64(struct ipr_cmnd *ipr_cmd,
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));
+ cpu_to_be64(dma_addr + offsetof(struct ipr_cmnd, i.ata_ioadl.ioadl64));
for_each_sg(qc->sg, sg, qc->n_elem, si) {
ioadl64->flags = cpu_to_be32(ioadl_flags);
@@ -6739,6 +6739,7 @@ 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) {
@@ -6750,6 +6751,13 @@ static int ipr_ioa_bringdown_done(struct ipr_cmnd *ipr_cmd)
ioa_cfg->in_reset_reload = 0;
ioa_cfg->reset_retries = 0;
+ 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();
+
list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
wake_up_all(&ioa_cfg->reset_wait_q);
LEAVE;
@@ -8651,7 +8659,7 @@ static void ipr_pci_perm_failure(struct pci_dev *pdev)
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->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);
diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h
index 21a6ff1ed5c6..a1fb840596ef 100644
--- a/drivers/scsi/ipr.h
+++ b/drivers/scsi/ipr.h
@@ -552,7 +552,7 @@ struct ipr_ioarcb_ata_regs { /* 22 bytes */
u8 hob_lbam;
u8 hob_lbah;
u8 ctl;
-}__attribute__ ((packed, aligned(4)));
+}__attribute__ ((packed, aligned(2)));
struct ipr_ioadl_desc {
__be32 flags_and_data_len;
diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c
index c3aa6c5457b9..96a26f454673 100644
--- a/drivers/scsi/isci/remote_device.c
+++ b/drivers/scsi/isci/remote_device.c
@@ -1085,7 +1085,7 @@ 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)) {
sci_change_state(&idev->sm, SCI_SMP_DEV_IDLE);
@@ -1098,7 +1098,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,
diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h
index 7674caae1d88..47a013fffae7 100644
--- a/drivers/scsi/isci/remote_device.h
+++ b/drivers/scsi/isci/remote_device.h
@@ -297,7 +297,7 @@ static inline struct isci_remote_device *rnc_to_dev(struct sci_remote_node_conte
static inline bool dev_is_expander(struct domain_device *dev)
{
- return dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV;
+ return dev->dev_type == SAS_EDGE_EXPANDER_DEVICE || dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE;
}
static inline void sci_remote_device_decrement_request_count(struct isci_remote_device *idev)
diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c
index 9594ab62702b..e3e3bcbd5a9f 100644
--- a/drivers/scsi/isci/request.c
+++ b/drivers/scsi/isci/request.c
@@ -2978,7 +2978,7 @@ static void sci_request_started_state_enter(struct sci_base_state_machine *sm)
/* all unaccelerated request types (non ssp or ncq) handled with
* substates
*/
- if (!task && dev->dev_type == SAS_END_DEV) {
+ if (!task && dev->dev_type == SAS_END_DEVICE) {
state = SCI_REQ_TASK_WAIT_TC_COMP;
} else if (task && task->task_proto == SAS_PROTOCOL_SMP) {
state = SCI_REQ_SMP_WAIT_RESP;
@@ -3101,7 +3101,7 @@ 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_is_sata(dev))
memset(&ireq->stp.cmd, 0, sizeof(ireq->stp.cmd));
@@ -3125,7 +3125,7 @@ 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_is_sata(dev)) {
+ 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));
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
index b6f19a1db780..9bb020ac089c 100644
--- a/drivers/scsi/isci/task.c
+++ b/drivers/scsi/isci/task.c
@@ -250,7 +250,7 @@ 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)
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c
index bdb81cda8401..161c98efade9 100644
--- a/drivers/scsi/libsas/sas_ata.c
+++ b/drivers/scsi/libsas/sas_ata.c
@@ -285,14 +285,14 @@ 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 |= SATA_DEV;
+ dev->tproto |= SAS_SATA_DEV;
- if (phy->attached_dev_type == SATA_PENDING)
- dev->dev_type = SATA_PENDING;
+ if (phy->attached_dev_type == SAS_SATA_PENDING)
+ dev->dev_type = SAS_SATA_PENDING;
else {
int res;
- dev->dev_type = SATA_DEV;
+ dev->dev_type = SAS_SATA_DEV;
res = sas_get_report_phy_sata(dev->parent, phy->phy_id,
&dev->sata_dev.rps_resp);
if (res) {
@@ -314,7 +314,7 @@ 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 != SATA_PENDING)
+ if (dev->dev_type != SAS_SATA_PENDING)
return 1;
/* hmmm, if this succeeds do we need to repost the domain_device to the
@@ -348,9 +348,9 @@ static int smp_ata_check_ready(struct ata_link *link)
return 0;
switch (ex_phy->attached_dev_type) {
- case SATA_PENDING:
+ case SAS_SATA_PENDING:
return 0;
- case SAS_END_DEV:
+ case SAS_END_DEVICE:
if (ex_phy->attached_sata_dev)
return sas_ata_clear_pending(dev, ex_phy);
default:
@@ -631,7 +631,7 @@ 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;
- if (dev->dev_type == SATA_PENDING)
+ if (dev->dev_type == SAS_SATA_PENDING)
return;
if ((fis->sector_count == 1 && /* ATA */
@@ -797,7 +797,7 @@ int sas_discover_sata(struct domain_device *dev)
{
int res;
- if (dev->dev_type == SATA_PM)
+ if (dev->dev_type == SAS_SATA_PM)
return -ENODEV;
sas_get_ata_command_set(dev);
diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c
index a0c3003e0c7d..62b58d38ce2e 100644
--- a/drivers/scsi/libsas/sas_discover.c
+++ b/drivers/scsi/libsas/sas_discover.c
@@ -39,11 +39,11 @@
void sas_init_dev(struct domain_device *dev)
{
switch (dev->dev_type) {
- case SAS_END_DEV:
+ case SAS_END_DEVICE:
INIT_LIST_HEAD(&dev->ssp_dev.eh_list_node);
break;
- case EDGE_DEV:
- case FANOUT_DEV:
+ 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;
@@ -93,9 +93,9 @@ static int sas_get_port_device(struct asd_sas_port *port)
if (fis->interrupt_reason == 1 && fis->lbal == 1 &&
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 {
struct sas_identify_frame *id =
@@ -109,21 +109,21 @@ static int sas_get_port_device(struct asd_sas_port *port)
dev->port = port;
switch (dev->dev_type) {
- case SATA_DEV:
+ case SAS_SATA_DEV:
rc = sas_ata_init(dev);
if (rc) {
rphy = NULL;
break;
}
/* fall through */
- case SAS_END_DEV:
+ 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;
@@ -156,7 +156,7 @@ static int sas_get_port_device(struct asd_sas_port *port)
dev->rphy = rphy;
get_device(&dev->rphy->dev);
- if (dev_is_sata(dev) || dev->dev_type == SAS_END_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);
@@ -315,7 +315,7 @@ void sas_free_device(struct kref *kref)
dev->phy = NULL;
/* remove the phys and ports, everything else should be gone */
- if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV)
+ if (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE || dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE)
kfree(dev->ex_dev.ex_phy);
if (dev_is_sata(dev) && dev->sata_dev.ap) {
@@ -343,7 +343,7 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d
spin_unlock_irq(&port->dev_list_lock);
spin_lock_irq(&ha->lock);
- if (dev->dev_type == SAS_END_DEV &&
+ 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--;
@@ -457,15 +457,15 @@ static void sas_discover_domain(struct work_struct *work)
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:
+ case SAS_SATA_DEV:
+ case SAS_SATA_PM:
#ifdef CONFIG_SCSI_SAS_ATA
error = sas_discover_sata(dev);
break;
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index f42b0e15410f..446b85110a1f 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -183,21 +183,21 @@ static char sas_route_char(struct domain_device *dev, struct ex_phy *phy)
}
}
-static enum sas_dev_type to_dev_type(struct discover_resp *dr)
+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 == NO_DEVICE && dr->attached_sata_dev &&
+ if (dr->attached_dev_type == SAS_PHY_UNUSED && dr->attached_sata_dev &&
dr->linkrate >= SAS_LINK_RATE_1_5_GBPS)
- return SATA_PENDING;
+ return SAS_SATA_PENDING;
else
return dr->attached_dev_type;
}
static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp)
{
- enum sas_dev_type dev_type;
+ enum sas_device_type dev_type;
enum sas_linkrate linkrate;
u8 sas_addr[SAS_ADDR_SIZE];
struct smp_resp *resp = rsp;
@@ -238,7 +238,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp)
/* 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 = NO_DEVICE;
+ phy->attached_dev_type = SAS_PHY_UNUSED;
if (!test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) {
phy->phy_id = phy_id;
goto skip;
@@ -259,7 +259,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp)
/* help some expanders that fail to zero sas_address in the 'no
* device' case
*/
- if (phy->attached_dev_type == NO_DEVICE ||
+ if (phy->attached_dev_type == SAS_PHY_UNUSED ||
phy->linkrate < SAS_LINK_RATE_1_5_GBPS)
memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
else
@@ -292,13 +292,13 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp)
out:
switch (phy->attached_dev_type) {
- case SATA_PENDING:
+ case SAS_SATA_PENDING:
type = "stp pending";
break;
- case NO_DEVICE:
+ case SAS_PHY_UNUSED:
type = "no device";
break;
- case SAS_END_DEV:
+ case SAS_END_DEVICE:
if (phy->attached_iproto) {
if (phy->attached_tproto)
type = "host+target";
@@ -311,8 +311,8 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp)
type = "ssp";
}
break;
- case EDGE_DEV:
- case FANOUT_DEV:
+ case SAS_EDGE_EXPANDER_DEVICE:
+ case SAS_FANOUT_EXPANDER_DEVICE:
type = "smp";
break;
default:
@@ -833,7 +833,7 @@ static struct domain_device *sas_ex_discover_end_dev(
} else
#endif
if (phy->attached_tproto & SAS_PROTOCOL_SSP) {
- child->dev_type = SAS_END_DEV;
+ child->dev_type = SAS_END_DEVICE;
rphy = sas_end_device_alloc(phy->port);
/* FIXME: error handling */
if (unlikely(!rphy))
@@ -932,11 +932,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;
@@ -1013,7 +1013,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);
@@ -1022,10 +1022,10 @@ 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 &&
- ex_phy->attached_dev_type != SATA_PENDING) {
+ 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) {
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),
@@ -1049,11 +1049,11 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)
}
switch (ex_phy->attached_dev_type) {
- case SAS_END_DEV:
- case SATA_PENDING:
+ 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",
@@ -1067,7 +1067,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)
memcpy(dev->port->disc.fanout_sas_addr,
ex_phy->attached_sas_addr, SAS_ADDR_SIZE);
/* fallthrough */
- case EDGE_DEV:
+ case SAS_EDGE_EXPANDER_DEVICE:
child = sas_ex_discover_expander(dev, phy_id);
break;
default:
@@ -1111,8 +1111,8 @@ 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 ((phy->attached_dev_type == SAS_EDGE_EXPANDER_DEVICE ||
+ phy->attached_dev_type == SAS_FANOUT_EXPANDER_DEVICE) &&
phy->routing_attr == SUBTRACTIVE_ROUTING) {
memcpy(sub_addr, phy->attached_sas_addr,SAS_ADDR_SIZE);
@@ -1130,8 +1130,8 @@ static int sas_check_level_subtractive_boundary(struct domain_device *dev)
u8 sub_addr[8] = {0, };
list_for_each_entry(child, &ex->children, siblings) {
- if (child->dev_type != EDGE_DEV &&
- child->dev_type != FANOUT_DEV)
+ if (child->dev_type != SAS_EDGE_EXPANDER_DEVICE &&
+ child->dev_type != SAS_FANOUT_EXPANDER_DEVICE)
continue;
if (sub_addr[0] == 0) {
sas_find_sub_addr(child, sub_addr);
@@ -1208,7 +1208,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++) {
@@ -1218,8 +1218,8 @@ 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 ((phy->attached_dev_type == SAS_FANOUT_EXPANDER_DEVICE ||
+ phy->attached_dev_type == SAS_EDGE_EXPANDER_DEVICE) &&
phy->routing_attr == SUBTRACTIVE_ROUTING) {
if (!sub_sas_addr)
@@ -1245,8 +1245,8 @@ static void sas_print_parent_topology_bug(struct domain_device *child,
struct ex_phy *child_phy)
{
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;
@@ -1321,8 +1321,8 @@ static int sas_check_parent_topology(struct domain_device *child)
if (!child->parent)
return 0;
- if (child->parent->dev_type != EDGE_DEV &&
- child->parent->dev_type != FANOUT_DEV)
+ if (child->parent->dev_type != SAS_EDGE_EXPANDER_DEVICE &&
+ child->parent->dev_type != SAS_FANOUT_EXPANDER_DEVICE)
return 0;
parent_ex = &child->parent->ex_dev;
@@ -1341,8 +1341,8 @@ static int sas_check_parent_topology(struct domain_device *child)
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) {
+ case SAS_EDGE_EXPANDER_DEVICE:
+ if (child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) {
if (parent_phy->routing_attr != SUBTRACTIVE_ROUTING ||
child_phy->routing_attr != TABLE_ROUTING) {
sas_print_parent_topology_bug(child, parent_phy, child_phy);
@@ -1366,7 +1366,7 @@ static int sas_check_parent_topology(struct domain_device *child)
}
}
break;
- case FANOUT_DEV:
+ case SAS_FANOUT_EXPANDER_DEVICE:
if (parent_phy->routing_attr != TABLE_ROUTING ||
child_phy->routing_attr != SUBTRACTIVE_ROUTING) {
sas_print_parent_topology_bug(child, parent_phy, child_phy);
@@ -1619,8 +1619,8 @@ 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->dev_type == SAS_EDGE_EXPANDER_DEVICE ||
+ dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE) {
struct sas_expander_device *ex =
rphy_to_expander_device(dev->rphy);
@@ -1720,7 +1720,7 @@ static int sas_get_phy_change_count(struct domain_device *dev,
}
static int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id,
- u8 *sas_addr, enum sas_dev_type *type)
+ u8 *sas_addr, enum sas_device_type *type)
{
int res;
struct smp_resp *disc_resp;
@@ -1849,7 +1849,7 @@ static int sas_find_bcast_dev(struct domain_device *dev,
SAS_DPRINTK("Expander phys DID NOT change\n");
}
list_for_each_entry(ch, &ex->children, siblings) {
- if (ch->dev_type == EDGE_DEV || ch->dev_type == FANOUT_DEV) {
+ if (ch->dev_type == SAS_EDGE_EXPANDER_DEVICE || ch->dev_type == SAS_FANOUT_EXPANDER_DEVICE) {
res = sas_find_bcast_dev(ch, src_dev);
if (*src_dev)
return res;
@@ -1866,8 +1866,8 @@ static void sas_unregister_ex_tree(struct asd_sas_port *port, struct domain_devi
list_for_each_entry_safe(child, n, &ex->children, siblings) {
set_bit(SAS_DEV_GONE, &child->state);
- if (child->dev_type == EDGE_DEV ||
- child->dev_type == FANOUT_DEV)
+ if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE ||
+ child->dev_type == SAS_FANOUT_EXPANDER_DEVICE)
sas_unregister_ex_tree(port, child);
else
sas_unregister_dev(port, child);
@@ -1887,8 +1887,8 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent,
if (SAS_ADDR(child->sas_addr) ==
SAS_ADDR(phy->attached_sas_addr)) {
set_bit(SAS_DEV_GONE, &child->state);
- if (child->dev_type == EDGE_DEV ||
- child->dev_type == FANOUT_DEV)
+ if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE ||
+ child->dev_type == SAS_FANOUT_EXPANDER_DEVICE)
sas_unregister_ex_tree(parent->port, child);
else
sas_unregister_dev(parent->port, child);
@@ -1916,8 +1916,8 @@ 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 (child->dev_type == SAS_EDGE_EXPANDER_DEVICE ||
+ child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) {
struct sas_expander_device *ex =
rphy_to_expander_device(child->rphy);
@@ -1970,8 +1970,8 @@ static int sas_discover_new(struct domain_device *dev, int phy_id)
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 (child->dev_type == SAS_EDGE_EXPANDER_DEVICE ||
+ child->dev_type == SAS_FANOUT_EXPANDER_DEVICE)
res = sas_discover_bfs_by_root(child);
break;
}
@@ -1979,16 +1979,16 @@ static int sas_discover_new(struct domain_device *dev, int phy_id)
return res;
}
-static bool dev_type_flutter(enum sas_dev_type new, enum sas_dev_type old)
+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_DEV to SATA_PENDING the link needs recovery
+ * SAS_END_DEVICE to SAS_SATA_PENDING the link needs recovery
*/
- if ((old == SATA_PENDING && new == SAS_END_DEV) ||
- (old == SAS_END_DEV && new == SATA_PENDING))
+ if ((old == SAS_SATA_PENDING && new == SAS_END_DEVICE) ||
+ (old == SAS_END_DEVICE && new == SAS_SATA_PENDING))
return true;
return false;
@@ -1998,7 +1998,7 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last)
{
struct expander_device *ex = &dev->ex_dev;
struct ex_phy *phy = &ex->ex_phy[phy_id];
- enum sas_dev_type type = NO_DEVICE;
+ enum sas_device_type type = SAS_PHY_UNUSED;
u8 sas_addr[8];
int res;
@@ -2032,7 +2032,7 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last)
sas_ex_phy_discover(dev, phy_id);
- if (ata_dev && phy->attached_dev_type == SATA_PENDING)
+ if (ata_dev && phy->attached_dev_type == SAS_SATA_PENDING)
action = ", needs recovery";
SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter%s\n",
SAS_ADDR(dev->sas_addr), phy_id, action);
diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h
index 1de67964e5a1..7e7ba83f0a21 100644
--- a/drivers/scsi/libsas/sas_internal.h
+++ b/drivers/scsi/libsas/sas_internal.h
@@ -131,16 +131,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 SATA_PENDING:
+ 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:
diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c
index 1398b714c018..d3c5297c6c89 100644
--- a/drivers/scsi/libsas/sas_port.c
+++ b/drivers/scsi/libsas/sas_port.c
@@ -69,7 +69,7 @@ static void sas_resume_port(struct asd_sas_phy *phy)
continue;
}
- if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) {
+ if (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE || dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE) {
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];
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 7706c99ec8bb..bcc56cac4fd8 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -46,10 +46,15 @@ struct lpfc_sli2_slim;
#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_MIN_SG_SLI4_BUF_SZ 0x800 /* based on LPFC_DEFAULT_SG_SEG_CNT */
+#define LPFC_MAX_SG_SLI4_SEG_CNT_DIF 128 /* sg element count per scsi cmnd */
+#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_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_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 */
@@ -66,8 +71,10 @@ struct lpfc_sli2_slim;
* 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 */
+/* 1 Second */
+#define QUEUE_RAMP_DOWN_INTERVAL (msecs_to_jiffies(1000 * 1))
+/* 5 minutes */
+#define QUEUE_RAMP_UP_INTERVAL (msecs_to_jiffies(1000 * 300))
/* Number of exchanges reserved for discovery to complete */
#define LPFC_DISC_IOCB_BUFF_COUNT 20
@@ -671,6 +678,7 @@ 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;
@@ -701,9 +709,11 @@ struct lpfc_hba {
uint32_t cfg_poll_tmo;
uint32_t cfg_use_msi;
uint32_t cfg_fcp_imax;
+ uint32_t cfg_fcp_cpu_map;
uint32_t cfg_fcp_wq_count;
uint32_t cfg_fcp_eq_count;
uint32_t cfg_fcp_io_channel;
+ uint32_t cfg_total_seg_cnt;
uint32_t cfg_sg_seg_cnt;
uint32_t cfg_prot_sg_seg_cnt;
uint32_t cfg_sg_dma_buf_size;
@@ -804,8 +814,10 @@ struct lpfc_hba {
uint64_t bg_reftag_err_cnt;
/* 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;
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index 9290713af253..3c5625b8b1f4 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -674,6 +674,9 @@ lpfc_do_offline(struct lpfc_hba *phba, uint32_t type)
int i;
int rc;
+ if (phba->pport->fc_flag & FC_OFFLINE_MODE)
+ return 0;
+
init_completion(&online_compl);
rc = lpfc_workq_post_event(phba, &status, &online_compl,
LPFC_EVT_OFFLINE_PREP);
@@ -741,7 +744,8 @@ lpfc_selective_reset(struct lpfc_hba *phba)
int status = 0;
int rc;
- if (!phba->cfg_enable_hba_reset)
+ if ((!phba->cfg_enable_hba_reset) ||
+ (phba->pport->fc_flag & FC_OFFLINE_MODE))
return -EACCES;
status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
@@ -895,6 +899,7 @@ lpfc_sli4_pdev_reg_request(struct lpfc_hba *phba, uint32_t opcode)
pci_disable_sriov(pdev);
phba->cfg_sriov_nr_virtfn = 0;
}
+
status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
if (status != 0)
@@ -2801,6 +2806,8 @@ lpfc_topology_store(struct device *dev, struct device_attribute *attr,
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;
@@ -3792,6 +3799,141 @@ lpfc_fcp_imax_init(struct lpfc_hba *phba, int val)
static DEVICE_ATTR(lpfc_fcp_imax, S_IRUGO | S_IWUSR,
lpfc_fcp_imax_show, lpfc_fcp_imax_store);
+/**
+ * lpfc_state_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 idx, len = 0;
+
+ if ((phba->sli_rev != LPFC_SLI_REV4) ||
+ (phba->intr_type != MSIX))
+ return len;
+
+ switch (phba->cfg_fcp_cpu_map) {
+ case 0:
+ len += snprintf(buf + len, PAGE_SIZE-len,
+ "fcp_cpu_map: No mapping (%d)\n",
+ phba->cfg_fcp_cpu_map);
+ return len;
+ case 1:
+ len += snprintf(buf + len, PAGE_SIZE-len,
+ "fcp_cpu_map: HBA centric mapping (%d): "
+ "%d online CPUs\n",
+ phba->cfg_fcp_cpu_map,
+ phba->sli4_hba.num_online_cpu);
+ break;
+ case 2:
+ len += snprintf(buf + len, PAGE_SIZE-len,
+ "fcp_cpu_map: Driver centric mapping (%d): "
+ "%d online CPUs\n",
+ phba->cfg_fcp_cpu_map,
+ phba->sli4_hba.num_online_cpu);
+ break;
+ }
+
+ cpup = phba->sli4_hba.cpu_map;
+ for (idx = 0; idx < phba->sli4_hba.num_present_cpu; idx++) {
+ if (cpup->irq == LPFC_VECTOR_MAP_EMPTY)
+ len += snprintf(buf + len, PAGE_SIZE-len,
+ "CPU %02d io_chan %02d "
+ "physid %d coreid %d\n",
+ idx, cpup->channel_id, cpup->phys_id,
+ cpup->core_id);
+ else
+ len += snprintf(buf + len, PAGE_SIZE-len,
+ "CPU %02d io_chan %02d "
+ "physid %d coreid %d IRQ %d\n",
+ idx, cpup->channel_id, cpup->phys_id,
+ cpup->core_id, cpup->irq);
+
+ cpup++;
+ }
+ 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)
+{
+ int status = -EINVAL;
+ return status;
+}
+
+/*
+# lpfc_fcp_cpu_map: Defines how to map CPUs to IRQ vectors
+# for the HBA.
+#
+# Value range is [0 to 2]. Default value is LPFC_DRIVER_CPU_MAP (2).
+# 0 - Do not affinitze IRQ vectors
+# 1 - Affintize HBA vectors with respect to each HBA
+# (start with CPU0 for each HBA)
+# 2 - Affintize HBA vectors with respect to the entire driver
+# (round robin thru all CPUs across all HBAs)
+*/
+static int lpfc_fcp_cpu_map = LPFC_DRIVER_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 fcp_cpu_map: %d out of range, using default\n",
+ val);
+ phba->cfg_fcp_cpu_map = LPFC_DRIVER_CPU_MAP;
+
+ return 0;
+}
+
+static DEVICE_ATTR(lpfc_fcp_cpu_map, S_IRUGO | S_IWUSR,
+ lpfc_fcp_cpu_map_show, lpfc_fcp_cpu_map_store);
+
/*
# lpfc_fcp_class: Determines FC class to use for the FCP protocol.
# Value range is [2,3]. Default value is 3.
@@ -4009,12 +4151,11 @@ LPFC_ATTR_R(enable_bg, 0, 0, 1, "Enable BlockGuard Support");
# 0 = disabled (default)
# 1 = enabled
# Value range is [0,1]. Default value is 0.
+#
+# This feature in under investigation and may be supported in the future.
*/
unsigned int lpfc_fcp_look_ahead = LPFC_LOOK_AHEAD_OFF;
-module_param(lpfc_fcp_look_ahead, uint, S_IRUGO);
-MODULE_PARM_DESC(lpfc_fcp_look_ahead, "Look ahead for completions");
-
/*
# lpfc_prot_mask: i
# - Bit mask of host protection capabilities used to register with the
@@ -4071,16 +4212,23 @@ MODULE_PARM_DESC(lpfc_delay_discovery,
/*
* lpfc_sg_seg_cnt - Initial Maximum DMA Segment Count
- * This value can be set to values between 64 and 256. The default value is
+ * 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 layer
* will be allowed to request I/Os of 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");
+/*
+ * This parameter will be depricated, the driver cannot limit the
+ * protection data s/g list.
+ */
+LPFC_ATTR_R(prot_sg_seg_cnt, LPFC_DEFAULT_SG_SEG_CNT,
+ LPFC_DEFAULT_SG_SEG_CNT, LPFC_MAX_SG_SEG_CNT,
+ "Max Protection Scatter Gather Segment Count");
struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_bg_info,
@@ -4141,6 +4289,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_poll_tmo,
&dev_attr_lpfc_use_msi,
&dev_attr_lpfc_fcp_imax,
+ &dev_attr_lpfc_fcp_cpu_map,
&dev_attr_lpfc_fcp_wq_count,
&dev_attr_lpfc_fcp_eq_count,
&dev_attr_lpfc_fcp_io_channel,
@@ -5123,6 +5272,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
lpfc_enable_rrq_init(phba, lpfc_enable_rrq);
lpfc_use_msi_init(phba, lpfc_use_msi);
lpfc_fcp_imax_init(phba, lpfc_fcp_imax);
+ lpfc_fcp_cpu_map_init(phba, lpfc_fcp_cpu_map);
lpfc_fcp_wq_count_init(phba, lpfc_fcp_wq_count);
lpfc_fcp_eq_count_init(phba, lpfc_fcp_eq_count);
lpfc_fcp_io_channel_init(phba, lpfc_fcp_io_channel);
diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c
index 888666892004..094be2cad65b 100644
--- a/drivers/scsi/lpfc/lpfc_bsg.c
+++ b/drivers/scsi/lpfc/lpfc_bsg.c
@@ -219,26 +219,35 @@ lpfc_bsg_copy_data(struct lpfc_dmabuf *dma_buffers,
unsigned int transfer_bytes, bytes_copied = 0;
unsigned int sg_offset, dma_offset;
unsigned char *dma_address, *sg_address;
- struct scatterlist *sgel;
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;
- sgel = bsg_buffers->sg_list;
+ 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 && sgel &&
+ 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 = sg_virt(sgel) + sg_offset;
- transfer_bytes = sgel->length - sg_offset;
+ sg_address = miter.addr + sg_offset;
+ transfer_bytes = miter.length - sg_offset;
} else {
- sg_address = sg_virt(sgel);
- transfer_bytes = sgel->length;
+ sg_address = miter.addr;
+ transfer_bytes = miter.length;
}
if (bytes_to_transfer < transfer_bytes)
transfer_bytes = bytes_to_transfer;
@@ -252,12 +261,14 @@ lpfc_bsg_copy_data(struct lpfc_dmabuf *dma_buffers,
sg_offset += transfer_bytes;
bytes_to_transfer -= transfer_bytes;
bytes_copied += transfer_bytes;
- if (sg_offset >= sgel->length) {
+ if (sg_offset >= miter.length) {
sg_offset = 0;
- sgel = sg_next(sgel);
+ 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;
@@ -471,6 +482,7 @@ lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job)
cmdiocbq->context1 = dd_data;
cmdiocbq->context2 = cmp;
cmdiocbq->context3 = bmp;
+ cmdiocbq->context_un.ndlp = ndlp;
dd_data->type = TYPE_IOCB;
dd_data->set_job = job;
dd_data->context_un.iocb.cmdiocbq = cmdiocbq;
@@ -1508,6 +1520,7 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag,
ctiocb->context1 = dd_data;
ctiocb->context2 = cmp;
ctiocb->context3 = bmp;
+ ctiocb->context_un.ndlp = ndlp;
ctiocb->iocb_cmpl = lpfc_issue_ct_rsp_cmp;
dd_data->type = TYPE_IOCB;
@@ -2576,7 +2589,8 @@ static int lpfcdiag_loop_get_xri(struct lpfc_hba *phba, uint16_t rpi,
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);
+ msecs_to_jiffies(1000 *
+ ((phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT)));
if (list_empty(&evt->events_to_see))
ret_val = (time_left) ? -EINTR : -ETIMEDOUT;
else {
@@ -3151,7 +3165,8 @@ 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);
+ msecs_to_jiffies(1000 *
+ ((phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT)));
evt->waiting = 0;
if (list_empty(&evt->events_to_see)) {
rc = (time_left) ? -EINTR : -ETIMEDOUT;
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index 7631893ae005..d41456e5f814 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -470,3 +470,4 @@ int lpfc_sli4_xri_sgl_update(struct lpfc_hba *);
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 *);
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index 7bff3a19af56..ae1a07c57cae 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -1811,7 +1811,8 @@ lpfc_fdmi_timeout_handler(struct lpfc_vport *vport)
if (init_utsname()->nodename[0] != '\0')
lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA);
else
- mod_timer(&vport->fc_fdmitmo, jiffies + HZ * 60);
+ mod_timer(&vport->fc_fdmitmo, jiffies +
+ msecs_to_jiffies(1000 * 60));
}
return;
}
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index bbed8471bf0b..3cae0a92e8bd 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -29,6 +29,7 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
#include "lpfc_sli.h"
@@ -238,7 +239,10 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
icmd->un.elsreq64.remoteID = did; /* DID */
icmd->ulpCommand = CMD_ELS_REQUEST64_CR;
- icmd->ulpTimeout = phba->fc_ratov * 2;
+ if (elscmd == ELS_CMD_FLOGI)
+ icmd->ulpTimeout = FF_DEF_RATOV * 2;
+ else
+ icmd->ulpTimeout = phba->fc_ratov * 2;
} else {
icmd->un.xseq64.bdl.addrHigh = putPaddrHigh(pbuflist->phys);
icmd->un.xseq64.bdl.addrLow = putPaddrLow(pbuflist->phys);
@@ -308,16 +312,20 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
/* 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"
+ " fc_flag:x%x\n",
elscmd, did, elsiocb->iotag,
- vport->port_state);
+ vport->port_state,
+ 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 fc_flag x%x\n",
elscmd, ndlp->nlp_DID, elsiocb->iotag,
- cmdSize);
+ cmdSize, vport->port_state,
+ vport->fc_flag);
}
return elsiocb;
@@ -909,6 +917,23 @@ lpfc_cmpl_els_flogi_nport(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
spin_lock_irq(shost->host_lock);
vport->fc_flag |= FC_PT2PT;
spin_unlock_irq(shost->host_lock);
+ /* 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);
+
+ /* The FC_VFI_REGISTERED flag will get clear in the cmpl
+ * handler for unreg_vfi, but if we don't force the
+ * FC_VFI_REGISTERED flag then the reg_vfi mailbox could be
+ * built with the update bit set instead of just the vp bit to
+ * change the Nport ID. We need to have the vp set and the
+ * Upd cleared on topology changes.
+ */
+ spin_lock_irq(shost->host_lock);
+ vport->fc_flag &= ~FC_VFI_REGISTERED;
+ spin_unlock_irq(shost->host_lock);
+ phba->fc_topology_changed = 0;
+ lpfc_issue_reg_vfi(vport);
+ }
/* Start discovery - this should just do CLEAR_LA */
lpfc_disc_start(vport);
@@ -1030,9 +1055,19 @@ stop_rr_fcf_flogi:
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)
- lpfc_sli4_unreg_all_rpis(vport);
+ (vport->fc_prevDID != vport->fc_myDID) ||
+ phba->fc_topology_changed)) {
+ if (vport->fc_flag & FC_VFI_REGISTERED) {
+ if (phba->fc_topology_changed) {
+ lpfc_unregister_fcf_prep(phba);
+ spin_lock_irq(shost->host_lock);
+ vport->fc_flag &= ~FC_VFI_REGISTERED;
+ spin_unlock_irq(shost->host_lock);
+ phba->fc_topology_changed = 0;
+ } else {
+ lpfc_sli4_unreg_all_rpis(vport);
+ }
+ }
lpfc_issue_reg_vfi(vport);
lpfc_nlp_put(ndlp);
goto out;
@@ -1054,10 +1089,11 @@ stop_rr_fcf_flogi:
/* 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",
+ "0101 FLOGI completes successfully, I/O tag:x%x, "
+ "Data: x%x x%x x%x x%x x%x x%x\n", cmdiocb->iotag,
irsp->un.ulpWord[4], sp->cmn.e_d_tov,
- sp->cmn.w2.r_a_tov, sp->cmn.edtovResolution);
+ sp->cmn.w2.r_a_tov, sp->cmn.edtovResolution,
+ vport->port_state, vport->fc_flag);
if (vport->port_state == LPFC_FLOGI) {
/*
@@ -5047,6 +5083,8 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
struct ls_rjt stat;
uint32_t cmd, did;
int rc;
+ uint32_t fc_flag = 0;
+ uint32_t port_state = 0;
cmd = *lp++;
sp = (struct serv_parm *) lp;
@@ -5113,16 +5151,25 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
* will be.
*/
vport->fc_myDID = PT2PT_LocalID;
- }
+ } else
+ 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;
vport->fc_flag |= FC_PT2PT;
vport->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
+ vport->port_state = LPFC_FLOGI;
spin_unlock_irq(shost->host_lock);
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "3311 Rcv Flogi PS x%x new PS x%x "
+ "fc_flag x%x new fc_flag x%x\n",
+ port_state, vport->port_state,
+ fc_flag, vport->fc_flag);
/*
* We temporarily set fc_myDID to make it look like we are
@@ -6241,7 +6288,8 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport)
}
if (!list_empty(&phba->sli.ring[LPFC_ELS_RING].txcmplq))
- mod_timer(&vport->els_tmofunc, jiffies + HZ * timeout);
+ mod_timer(&vport->els_tmofunc,
+ jiffies + msecs_to_jiffies(1000 * timeout));
}
/**
@@ -6612,7 +6660,9 @@ 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);
+ "Data: x%x x%x x%x x%x\n",
+ cmd, did, vport->port_state, vport->fc_flag,
+ vport->fc_myDID, vport->fc_prevDID);
switch (cmd) {
case ELS_CMD_PLOGI:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
@@ -6621,6 +6671,19 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
phba->fc_stat.elsRcvPLOGI++;
ndlp = lpfc_plogi_confirm_nport(phba, payload, ndlp);
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ (phba->pport->fc_flag & FC_PT2PT)) {
+ 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 = elsiocb->iocb.un.rcvels.parmRo;
+ 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);
@@ -6630,6 +6693,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
rjt_exp = LSEXP_NOTHING_MORE;
break;
}
+ shost = lpfc_shost_from_vport(vport);
if (vport->port_state < LPFC_DISC_AUTH) {
if (!(phba->pport->fc_flag & FC_PT2PT) ||
(phba->pport->fc_flag & FC_PT2PT_PLOGI)) {
@@ -6641,9 +6705,18 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
* another NPort and the other side has initiated
* the PLOGI before responding to our FLOGI.
*/
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ (phba->fc_topology_changed ||
+ vport->fc_myDID != vport->fc_prevDID)) {
+ lpfc_unregister_fcf_prep(phba);
+ spin_lock_irq(shost->host_lock);
+ vport->fc_flag &= ~FC_VFI_REGISTERED;
+ spin_unlock_irq(shost->host_lock);
+ phba->fc_topology_changed = 0;
+ lpfc_issue_reg_vfi(vport);
+ }
}
- shost = lpfc_shost_from_vport(vport);
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_TARGET_REMOVE;
spin_unlock_irq(shost->host_lock);
@@ -7002,8 +7075,11 @@ lpfc_do_scr_ns_plogi(struct lpfc_hba *phba, struct lpfc_vport *vport)
spin_lock_irq(shost->host_lock);
if (vport->fc_flag & FC_DISC_DELAYED) {
spin_unlock_irq(shost->host_lock);
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ "3334 Delay fc port discovery for %d seconds\n",
+ phba->fc_ratov);
mod_timer(&vport->delayed_disc_tmo,
- jiffies + HZ * phba->fc_ratov);
+ jiffies + msecs_to_jiffies(1000 * phba->fc_ratov));
return;
}
spin_unlock_irq(shost->host_lock);
@@ -7287,7 +7363,7 @@ lpfc_retry_pport_discovery(struct lpfc_hba *phba)
return;
shost = lpfc_shost_from_vport(phba->pport);
- 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);
@@ -7791,7 +7867,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;
}
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index 326e05a65a73..0f6e2548f35d 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -160,11 +160,12 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
if (!list_empty(&evtp->evt_listp))
return;
+ evtp->evt_arg1 = lpfc_nlp_get(ndlp);
+
spin_lock_irq(&phba->hbalock);
/* We need to hold the node by incrementing the reference
* count until this queued work is done
*/
- 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);
@@ -1008,9 +1009,6 @@ lpfc_linkup(struct lpfc_hba *phba)
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);
return 0;
}
@@ -1436,7 +1434,8 @@ lpfc_register_fcf(struct lpfc_hba *phba)
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) {
+ if (phba->pport->port_state != LPFC_FLOGI &&
+ phba->pport->fc_flag & FC_FABRIC) {
phba->hba_flag |= FCF_RR_INPROG;
spin_unlock_irq(&phba->hbalock);
lpfc_initial_flogi(phba->pport);
@@ -2270,8 +2269,11 @@ 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%x\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,
@@ -2796,7 +2798,19 @@ 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_MBOX,
+ "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) {
@@ -2894,9 +2908,14 @@ lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
goto out_free_mem;
}
- /* If the VFI is already registered, there is nothing else to do */
+ /* 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 (vport->fc_flag & FC_VFI_REGISTERED)
- goto out_free_mem;
+ if (!(phba->sli_rev == LPFC_SLI_REV4 &&
+ vport->fc_flag & FC_PT2PT))
+ goto out_free_mem;
/* The VPI is implicitly registered when the VFI is registered */
spin_lock_irq(shost->host_lock);
@@ -2913,6 +2932,13 @@ lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
goto out_free_mem;
}
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI,
+ "3313 cmpl reg vfi port_state:%x fc_flag:%x 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 or for NPort pt2pt,
@@ -2925,7 +2951,10 @@ lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
/* Use loop map to make discovery list */
lpfc_disc_list_loopmap(vport);
/* Start discovery */
- lpfc_disc_start(vport);
+ if (vport->fc_flag & FC_PT2PT)
+ vport->port_state = LPFC_VPORT_READY;
+ else
+ lpfc_disc_start(vport);
} else {
lpfc_start_fdiscs(phba);
lpfc_do_scr_ns_plogi(phba, vport);
@@ -3007,6 +3036,15 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, struct lpfc_mbx_read_top *la)
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 Toplogy 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;
@@ -4235,7 +4273,7 @@ lpfc_set_disctmo(struct lpfc_vport *vport)
tmo, vport->port_state, vport->fc_flag);
}
- mod_timer(&vport->fc_disctmo, jiffies + HZ * tmo);
+ mod_timer(&vport->fc_disctmo, jiffies + msecs_to_jiffies(1000 * tmo));
spin_lock_irq(shost->host_lock);
vport->fc_flag |= FC_DISC_TMO;
spin_unlock_irq(shost->host_lock);
@@ -4949,8 +4987,12 @@ lpfc_disc_start(struct lpfc_vport *vport)
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;
@@ -4983,11 +5025,13 @@ 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) &&
(phba->sli_rev < LPFC_SLI_REV4)) {
+ if (vport->port_type == LPFC_PHYSICAL_PORT)
+ lpfc_issue_clear_la(phba, vport);
lpfc_issue_reg_vpi(phba, vport);
return;
}
@@ -5410,7 +5454,8 @@ lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
if (vport->cfg_fdmi_on == 1)
lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA);
else
- mod_timer(&vport->fc_fdmitmo, jiffies + HZ * 60);
+ mod_timer(&vport->fc_fdmitmo,
+ jiffies + msecs_to_jiffies(1000 * 60));
/* decrement the node reference count held for this callback
* function.
@@ -5855,7 +5900,7 @@ lpfc_unregister_fcf_prep(struct lpfc_hba *phba)
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))
@@ -5883,6 +5928,20 @@ lpfc_unregister_fcf_prep(struct lpfc_hba *phba)
spin_unlock_irq(shost->host_lock);
}
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->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
+ phba->pport->vpi_state &= ~LPFC_VPI_REGISTERED;
+ spin_unlock_irq(shost->host_lock);
+ }
/* Cleanup any outstanding ELS commands */
lpfc_els_flush_all_cmd(phba);
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index e8c476031703..83700c18f468 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -1667,6 +1667,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;
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 1dd2f6f0a127..713a4613ec3a 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -200,6 +200,11 @@ struct lpfc_sli_intf {
#define LPFC_MAX_IMAX 5000000
#define LPFC_DEF_IMAX 50000
+#define LPFC_MIN_CPU_MAP 0
+#define LPFC_MAX_CPU_MAP 2
+#define LPFC_HBA_CPU_MAP 1
+#define LPFC_DRIVER_CPU_MAP 2 /* Default */
+
/* PORT_CAPABILITIES constants. */
#define LPFC_MAX_SUPPORTED_PAGES 8
@@ -621,7 +626,7 @@ struct lpfc_register {
#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 MAX_IF_TYPE_2_RESETS 6
#define LPFC_CTL_PORT_CTL_OFFSET 0x408
#define lpfc_sliport_ctrl_end_SHIFT 30
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 90b8b0515e23..cb465b253910 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -33,6 +33,7 @@
#include <linux/slab.h>
#include <linux/firmware.h>
#include <linux/miscdevice.h>
+#include <linux/percpu.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
@@ -58,6 +59,9 @@ char *_dump_buf_dif;
unsigned long _dump_buf_dif_order;
spinlock_t _dump_buf_lock;
+/* Used when mapping IRQ vectors in a driver centric manner */
+uint16_t lpfc_used_cpu[LPFC_MAX_CPU];
+
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_verify(struct lpfc_hba *);
@@ -541,13 +545,16 @@ 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 + msecs_to_jiffies(1000 * timeout));
/* Set up heart beat (HB) timer */
- mod_timer(&phba->hb_tmofunc, jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
+ mod_timer(&phba->hb_tmofunc,
+ jiffies + msecs_to_jiffies(1000 * LPFC_HB_MBOX_INTERVAL));
phba->hb_outstanding = 0;
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 + msecs_to_jiffies(1000 * LPFC_ERATT_POLL_INTERVAL));
if (phba->hba_flag & LINK_DISABLED) {
lpfc_printf_log(phba,
@@ -908,9 +915,9 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba)
psb->pCmd = NULL;
psb->status = IOSTAT_SUCCESS;
}
- 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);
+ spin_lock_irqsave(&phba->scsi_buf_list_put_lock, iflag);
+ list_splice(&aborts, &phba->lpfc_scsi_buf_list_put);
+ spin_unlock_irqrestore(&phba->scsi_buf_list_put_lock, iflag);
return 0;
}
@@ -1021,7 +1028,8 @@ lpfc_hb_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
!(phba->link_state == LPFC_HBA_ERROR) &&
!(phba->pport->load_flag & FC_UNLOADING))
mod_timer(&phba->hb_tmofunc,
- jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
+ jiffies +
+ msecs_to_jiffies(1000 * LPFC_HB_MBOX_INTERVAL));
return;
}
@@ -1064,15 +1072,18 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
spin_lock_irq(&phba->pport->work_port_lock);
- if (time_after(phba->last_completion_time + LPFC_HB_MBOX_INTERVAL * HZ,
- jiffies)) {
+ if (time_after(phba->last_completion_time +
+ msecs_to_jiffies(1000 * LPFC_HB_MBOX_INTERVAL),
+ jiffies)) {
spin_unlock_irq(&phba->pport->work_port_lock);
if (!phba->hb_outstanding)
mod_timer(&phba->hb_tmofunc,
- jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
+ jiffies +
+ msecs_to_jiffies(1000 * LPFC_HB_MBOX_INTERVAL));
else
mod_timer(&phba->hb_tmofunc,
- jiffies + HZ * LPFC_HB_MBOX_TIMEOUT);
+ jiffies +
+ msecs_to_jiffies(1000 * LPFC_HB_MBOX_TIMEOUT));
return;
}
spin_unlock_irq(&phba->pport->work_port_lock);
@@ -1104,7 +1115,8 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
if (!pmboxq) {
mod_timer(&phba->hb_tmofunc,
jiffies +
- HZ * LPFC_HB_MBOX_INTERVAL);
+ msecs_to_jiffies(1000 *
+ LPFC_HB_MBOX_INTERVAL));
return;
}
@@ -1120,7 +1132,8 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
phba->mbox_mem_pool);
mod_timer(&phba->hb_tmofunc,
jiffies +
- HZ * LPFC_HB_MBOX_INTERVAL);
+ msecs_to_jiffies(1000 *
+ LPFC_HB_MBOX_INTERVAL));
return;
}
phba->skipped_hb = 0;
@@ -1136,7 +1149,8 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
phba->skipped_hb = jiffies;
mod_timer(&phba->hb_tmofunc,
- jiffies + HZ * LPFC_HB_MBOX_TIMEOUT);
+ jiffies +
+ msecs_to_jiffies(1000 * LPFC_HB_MBOX_TIMEOUT));
return;
} else {
/*
@@ -1150,7 +1164,8 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
jiffies_to_msecs(jiffies
- phba->last_completion_time));
mod_timer(&phba->hb_tmofunc,
- jiffies + HZ * LPFC_HB_MBOX_TIMEOUT);
+ jiffies +
+ msecs_to_jiffies(1000 * LPFC_HB_MBOX_TIMEOUT));
}
}
}
@@ -1191,7 +1206,7 @@ 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, LPFC_MBX_NO_WAIT);
@@ -2633,6 +2648,7 @@ lpfc_online(struct lpfc_hba *phba)
struct lpfc_vport *vport;
struct lpfc_vport **vports;
int i;
+ bool vpis_cleared = false;
if (!phba)
return 0;
@@ -2656,6 +2672,10 @@ lpfc_online(struct lpfc_hba *phba)
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);
} else {
if (lpfc_sli_hba_setup(phba)) { /* Initialize SLI2/SLI3 HBA */
lpfc_unblock_mgmt_io(phba);
@@ -2672,8 +2692,13 @@ lpfc_online(struct lpfc_hba *phba)
vports[i]->fc_flag &= ~FC_OFFLINE_MODE;
if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)
vports[i]->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
- if (phba->sli_rev == LPFC_SLI_REV4)
+ if (phba->sli_rev == LPFC_SLI_REV4) {
vports[i]->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
+ if ((vpis_cleared) &&
+ (vports[i]->port_type !=
+ LPFC_PHYSICAL_PORT))
+ vports[i]->vpi = 0;
+ }
spin_unlock_irq(shost->host_lock);
}
lpfc_destroy_vport_work_array(phba, vports);
@@ -2833,16 +2858,30 @@ lpfc_scsi_free(struct lpfc_hba *phba)
struct lpfc_iocbq *io, *io_next;
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,
sb->dma_handle);
kfree(sb);
phba->total_scsi_bufs--;
}
- spin_unlock(&phba->scsi_buf_list_lock);
+ spin_unlock(&phba->scsi_buf_list_put_lock);
+
+ 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);
+ pci_pool_free(phba->lpfc_scsi_dma_buf_pool, sb->data,
+ sb->dma_handle);
+ kfree(sb);
+ phba->total_scsi_bufs--;
+ }
+ spin_unlock(&phba->scsi_buf_list_get_lock);
/* Release all the lpfc_iocbq entries maintained by this host. */
list_for_each_entry_safe(io, io_next, &phba->lpfc_iocb_list, list) {
@@ -2978,9 +3017,12 @@ lpfc_sli4_xri_sgl_update(struct lpfc_hba *phba)
phba->sli4_hba.scsi_xri_cnt,
phba->sli4_hba.scsi_xri_max);
- spin_lock_irq(&phba->scsi_buf_list_lock);
- list_splice_init(&phba->lpfc_scsi_buf_list, &scsi_sgl_list);
- spin_unlock_irq(&phba->scsi_buf_list_lock);
+ spin_lock_irq(&phba->scsi_buf_list_get_lock);
+ spin_lock_irq(&phba->scsi_buf_list_put_lock);
+ list_splice_init(&phba->lpfc_scsi_buf_list_get, &scsi_sgl_list);
+ list_splice(&phba->lpfc_scsi_buf_list_put, &scsi_sgl_list);
+ spin_unlock_irq(&phba->scsi_buf_list_put_lock);
+ spin_unlock_irq(&phba->scsi_buf_list_get_lock);
if (phba->sli4_hba.scsi_xri_cnt > phba->sli4_hba.scsi_xri_max) {
/* max scsi xri shrinked below the allocated scsi buffers */
@@ -2994,9 +3036,9 @@ lpfc_sli4_xri_sgl_update(struct lpfc_hba *phba)
psb->dma_handle);
kfree(psb);
}
- spin_lock_irq(&phba->scsi_buf_list_lock);
+ spin_lock_irq(&phba->scsi_buf_list_get_lock);
phba->sli4_hba.scsi_xri_cnt -= scsi_xri_cnt;
- spin_unlock_irq(&phba->scsi_buf_list_lock);
+ spin_unlock_irq(&phba->scsi_buf_list_get_lock);
}
/* update xris associated to remaining allocated scsi buffers */
@@ -3014,9 +3056,12 @@ lpfc_sli4_xri_sgl_update(struct lpfc_hba *phba)
psb->cur_iocbq.sli4_lxritag = lxri;
psb->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri];
}
- spin_lock_irq(&phba->scsi_buf_list_lock);
- list_splice_init(&scsi_sgl_list, &phba->lpfc_scsi_buf_list);
- spin_unlock_irq(&phba->scsi_buf_list_lock);
+ spin_lock_irq(&phba->scsi_buf_list_get_lock);
+ spin_lock_irq(&phba->scsi_buf_list_put_lock);
+ list_splice_init(&scsi_sgl_list, &phba->lpfc_scsi_buf_list_get);
+ INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put);
+ spin_unlock_irq(&phba->scsi_buf_list_put_lock);
+ spin_unlock_irq(&phba->scsi_buf_list_get_lock);
return 0;
@@ -3197,14 +3242,15 @@ int lpfc_scan_finished(struct Scsi_Host *shost, unsigned long time)
stat = 1;
goto finished;
}
- if (time >= 30 * HZ) {
+ if (time >= msecs_to_jiffies(30 * 1000)) {
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 >= msecs_to_jiffies(15 * 1000) &&
+ phba->link_state <= LPFC_LINK_DOWN) {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"0465 Link down longer than 15 "
"seconds. Continuing initialization\n");
@@ -3216,7 +3262,7 @@ 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 (vport->fc_map_cnt == 0 && time < msecs_to_jiffies(2 * 1000))
goto finished;
if ((phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE) != 0)
goto finished;
@@ -4215,7 +4261,8 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
* If there are other active VLinks present,
* re-instantiate the Vlink using FDISC.
*/
- mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);
+ mod_timer(&ndlp->nlp_delayfunc,
+ jiffies + msecs_to_jiffies(1000));
shost = lpfc_shost_from_vport(vport);
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_DELAY_TMO;
@@ -4707,23 +4754,52 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba)
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));
+ /* Initialize the host templates the configured values. */
+ lpfc_vport_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+ lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+
+ /* 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 * sizeof(struct ulp_bde64));
+
+ 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) * sizeof(struct ulp_bde64));
+
+ /* 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 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 */
@@ -4789,13 +4865,13 @@ lpfc_sli_driver_resource_unset(struct lpfc_hba *phba)
static int
lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
{
+ struct lpfc_vector_map_info *cpup;
struct lpfc_sli *psli;
LPFC_MBOXQ_t *mboxq;
- int rc, i, hbq_count, buf_size, dma_buf_size, max_buf_size;
+ int rc, i, hbq_count, max_buf_size;
uint8_t pn_page[LPFC_MAX_SUPPORTED_PAGES] = {0};
struct lpfc_mqe *mqe;
- int longs, sli_family;
- int sges_per_segment;
+ int longs;
/* Before proceed, wait for POST done and device ready */
rc = lpfc_sli4_post_status_check(phba);
@@ -4863,11 +4939,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
phba->fc_map[1] = LPFC_FCOE_FCF_MAP1;
phba->fc_map[2] = LPFC_FCOE_FCF_MAP2;
- /* With BlockGuard we can have multiple SGEs per Data Segemnt */
- sges_per_segment = 1;
- if (phba->cfg_enable_bg)
- sges_per_segment = 2;
-
/*
* For SLI4, instead of using ring 0 (LPFC_FCP_RING) for FCP commands
* we will associate a new ring, for each FCP fastpath EQ/CQ/WQ tuple.
@@ -4878,43 +4949,71 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
sizeof(struct lpfc_sli_ring), GFP_KERNEL);
if (!phba->sli.ring)
return -ENOMEM;
+
/*
- * Since the sg_tablesize is module parameter, the sg_dma_buf_size
+ * 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);
+ if (phba->cfg_sg_seg_cnt > LPFC_MAX_SGL_SEG_CNT - 2)
+ phba->cfg_sg_seg_cnt = LPFC_MAX_SGL_SEG_CNT - 2;
+
+ /*
+ * 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.
- * To insure that the scsi sgl does not cross a 4k page boundary only
- * sgl sizes of must be a power of 2.
*/
- buf_size = (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp) +
- (((phba->cfg_sg_seg_cnt * sges_per_segment) + 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;
+
+ if (phba->cfg_enable_bg) {
+ /*
+ * The scsi_buf for a T10-DIF I/O will hold the FCP cmnd,
+ * the FCP rsp, and a SGE 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 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_cmnd) +
+ 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 (phba->cfg_sg_seg_cnt > LPFC_MAX_SG_SLI4_SEG_CNT_DIF)
+ phba->cfg_sg_seg_cnt = LPFC_MAX_SG_SLI4_SEG_CNT_DIF;
+ } else {
+ /*
+ * The scsi_buf for a regular I/O will hold 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_cmnd) +
+ sizeof(struct fcp_rsp) +
+ ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct sli4_sge));
+
+ /* Total SGEs for scsi_sg_list */
+ phba->cfg_total_seg_cnt = phba->cfg_sg_seg_cnt + 2;
+ /*
+ * NOTE: if (phba->cfg_sg_seg_cnt + 2) <= 256 we only need
+ * to post 1 page for the SGL.
+ */
}
- 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 the host templates with the updated values. */
+ lpfc_vport_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+ lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+
+ 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);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT | LOG_FCP,
+ "9087 sg_tablesize:%d dmabuf_size:%d total_sge:%d\n",
+ phba->cfg_sg_seg_cnt, phba->cfg_sg_dma_buf_size,
+ phba->cfg_total_seg_cnt);
/* Initialize buffer queue management fields */
hbq_count = lpfc_sli_hbq_count();
@@ -5104,6 +5203,26 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
goto out_free_fcp_eq_hdl;
}
+ phba->sli4_hba.cpu_map = kzalloc((sizeof(struct lpfc_vector_map_info) *
+ phba->sli4_hba.num_present_cpu),
+ GFP_KERNEL);
+ if (!phba->sli4_hba.cpu_map) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3327 Failed allocate memory for msi-x "
+ "interrupt vector mapping\n");
+ rc = -ENOMEM;
+ goto out_free_msix;
+ }
+ /* Initialize io channels for round robin */
+ cpup = phba->sli4_hba.cpu_map;
+ rc = 0;
+ for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
+ cpup->channel_id = rc;
+ rc++;
+ if (rc >= phba->cfg_fcp_io_channel)
+ rc = 0;
+ }
+
/*
* Enable sr-iov virtual functions if supported and configured
* through the module parameter.
@@ -5123,6 +5242,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
return 0;
+out_free_msix:
+ kfree(phba->sli4_hba.msix_entries);
out_free_fcp_eq_hdl:
kfree(phba->sli4_hba.fcp_eq_hdl);
out_free_fcf_rr_bmask:
@@ -5152,6 +5273,11 @@ 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 to CPU mapping */
+ kfree(phba->sli4_hba.cpu_map);
+ phba->sli4_hba.num_present_cpu = 0;
+ phba->sli4_hba.num_online_cpu = 0;
+
/* Free memory allocated for msi-x interrupt vector entries */
kfree(phba->sli4_hba.msix_entries);
@@ -5260,8 +5386,10 @@ lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba)
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);
+ 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);
@@ -6696,6 +6824,7 @@ lpfc_sli4_queue_verify(struct lpfc_hba *phba)
int cfg_fcp_io_channel;
uint32_t cpu;
uint32_t i = 0;
+ uint32_t j = 0;
/*
@@ -6706,15 +6835,21 @@ lpfc_sli4_queue_verify(struct lpfc_hba *phba)
/* Sanity check on HBA EQ parameters */
cfg_fcp_io_channel = phba->cfg_fcp_io_channel;
- /* It doesn't make sense to have more io channels then CPUs */
- for_each_online_cpu(cpu) {
- i++;
+ /* It doesn't make sense to have more io channels then online CPUs */
+ for_each_present_cpu(cpu) {
+ if (cpu_online(cpu))
+ i++;
+ j++;
}
+ phba->sli4_hba.num_online_cpu = i;
+ phba->sli4_hba.num_present_cpu = j;
+
if (i < cfg_fcp_io_channel) {
lpfc_printf_log(phba,
KERN_ERR, LOG_INIT,
"3188 Reducing IO channels to match number of "
- "CPUs: from %d to %d\n", cfg_fcp_io_channel, i);
+ "online CPUs: from %d to %d\n",
+ cfg_fcp_io_channel, i);
cfg_fcp_io_channel = i;
}
@@ -7743,8 +7878,13 @@ lpfc_pci_function_reset(struct lpfc_hba *phba)
out:
/* Catch the not-ready port failure after a port reset. */
- if (num_resets >= MAX_IF_TYPE_2_RESETS)
+ if (num_resets >= MAX_IF_TYPE_2_RESETS) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3317 HBA not functional: IP Reset Failed "
+ "after (%d) retries, try: "
+ "echo fw_reset > board_mode\n", num_resets);
rc = -ENODEV;
+ }
return rc;
}
@@ -8209,6 +8349,269 @@ lpfc_sli_disable_intr(struct lpfc_hba *phba)
}
/**
+ * lpfc_find_next_cpu - Find next available CPU that matches the phys_id
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * Find next available CPU to use for IRQ to CPU affinity.
+ */
+static int
+lpfc_find_next_cpu(struct lpfc_hba *phba, uint32_t phys_id)
+{
+ struct lpfc_vector_map_info *cpup;
+ int cpu;
+
+ cpup = phba->sli4_hba.cpu_map;
+ for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) {
+ /* CPU must be online */
+ if (cpu_online(cpu)) {
+ if ((cpup->irq == LPFC_VECTOR_MAP_EMPTY) &&
+ (lpfc_used_cpu[cpu] == LPFC_VECTOR_MAP_EMPTY) &&
+ (cpup->phys_id == phys_id)) {
+ return cpu;
+ }
+ }
+ cpup++;
+ }
+
+ /*
+ * If we get here, we have used ALL CPUs for the specific
+ * phys_id. Now we need to clear out lpfc_used_cpu and start
+ * reusing CPUs.
+ */
+
+ for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) {
+ if (lpfc_used_cpu[cpu] == phys_id)
+ lpfc_used_cpu[cpu] = LPFC_VECTOR_MAP_EMPTY;
+ }
+
+ cpup = phba->sli4_hba.cpu_map;
+ for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) {
+ /* CPU must be online */
+ if (cpu_online(cpu)) {
+ if ((cpup->irq == LPFC_VECTOR_MAP_EMPTY) &&
+ (cpup->phys_id == phys_id)) {
+ return cpu;
+ }
+ }
+ cpup++;
+ }
+ return LPFC_VECTOR_MAP_EMPTY;
+}
+
+/**
+ * lpfc_sli4_set_affinity - Set affinity for HBA IRQ vectors
+ * @phba: pointer to lpfc hba data structure.
+ * @vectors: number of HBA vectors
+ *
+ * Affinitize MSIX IRQ vectors to CPUs. Try to equally spread vector
+ * affinization across multple physical CPUs (numa nodes).
+ * In addition, this routine will assign an IO channel for each CPU
+ * to use when issuing I/Os.
+ */
+static int
+lpfc_sli4_set_affinity(struct lpfc_hba *phba, int vectors)
+{
+ int i, idx, saved_chann, used_chann, cpu, phys_id;
+ int max_phys_id, num_io_channel, first_cpu;
+ struct lpfc_vector_map_info *cpup;
+#ifdef CONFIG_X86
+ struct cpuinfo_x86 *cpuinfo;
+#endif
+ struct cpumask *mask;
+ uint8_t chann[LPFC_FCP_IO_CHAN_MAX+1];
+
+ /* If there is no mapping, just return */
+ if (!phba->cfg_fcp_cpu_map)
+ return 1;
+
+ /* Init cpu_map array */
+ memset(phba->sli4_hba.cpu_map, 0xff,
+ (sizeof(struct lpfc_vector_map_info) *
+ phba->sli4_hba.num_present_cpu));
+
+ max_phys_id = 0;
+ phys_id = 0;
+ num_io_channel = 0;
+ first_cpu = LPFC_VECTOR_MAP_EMPTY;
+
+ /* Update CPU map with physical id and core id of each CPU */
+ cpup = phba->sli4_hba.cpu_map;
+ for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) {
+#ifdef CONFIG_X86
+ cpuinfo = &cpu_data(cpu);
+ cpup->phys_id = cpuinfo->phys_proc_id;
+ cpup->core_id = cpuinfo->cpu_core_id;
+#else
+ /* No distinction between CPUs for other platforms */
+ cpup->phys_id = 0;
+ cpup->core_id = 0;
+#endif
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3328 CPU physid %d coreid %d\n",
+ cpup->phys_id, cpup->core_id);
+
+ if (cpup->phys_id > max_phys_id)
+ max_phys_id = cpup->phys_id;
+ cpup++;
+ }
+
+ /* Now associate the HBA vectors with specific CPUs */
+ for (idx = 0; idx < vectors; idx++) {
+ cpup = phba->sli4_hba.cpu_map;
+ cpu = lpfc_find_next_cpu(phba, phys_id);
+ if (cpu == LPFC_VECTOR_MAP_EMPTY) {
+
+ /* Try for all phys_id's */
+ for (i = 1; i < max_phys_id; i++) {
+ phys_id++;
+ if (phys_id > max_phys_id)
+ phys_id = 0;
+ cpu = lpfc_find_next_cpu(phba, phys_id);
+ if (cpu == LPFC_VECTOR_MAP_EMPTY)
+ continue;
+ goto found;
+ }
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3329 Cannot set affinity:"
+ "Error mapping vector %d (%d)\n",
+ idx, vectors);
+ return 0;
+ }
+found:
+ cpup += cpu;
+ if (phba->cfg_fcp_cpu_map == LPFC_DRIVER_CPU_MAP)
+ lpfc_used_cpu[cpu] = phys_id;
+
+ /* Associate vector with selected CPU */
+ cpup->irq = phba->sli4_hba.msix_entries[idx].vector;
+
+ /* Associate IO channel with selected CPU */
+ cpup->channel_id = idx;
+ num_io_channel++;
+
+ if (first_cpu == LPFC_VECTOR_MAP_EMPTY)
+ first_cpu = cpu;
+
+ /* Now affinitize to the selected CPU */
+ mask = &cpup->maskbits;
+ cpumask_clear(mask);
+ cpumask_set_cpu(cpu, mask);
+ i = irq_set_affinity_hint(phba->sli4_hba.msix_entries[idx].
+ vector, mask);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3330 Set Affinity: CPU %d channel %d "
+ "irq %d (%x)\n",
+ cpu, cpup->channel_id,
+ phba->sli4_hba.msix_entries[idx].vector, i);
+
+ /* Spread vector mapping across multple physical CPU nodes */
+ phys_id++;
+ if (phys_id > max_phys_id)
+ phys_id = 0;
+ }
+
+ /*
+ * Finally fill in the IO channel for any remaining CPUs.
+ * At this point, all IO channels have been assigned to a specific
+ * MSIx vector, mapped to a specific CPU.
+ * Base the remaining IO channel assigned, to IO channels already
+ * assigned to other CPUs on the same phys_id.
+ */
+ for (i = 0; i <= max_phys_id; i++) {
+ /*
+ * If there are no io channels already mapped to
+ * this phys_id, just round robin thru the io_channels.
+ * Setup chann[] for round robin.
+ */
+ for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++)
+ chann[idx] = idx;
+
+ saved_chann = 0;
+ used_chann = 0;
+
+ /*
+ * First build a list of IO channels already assigned
+ * to this phys_id before reassigning the same IO
+ * channels to the remaining CPUs.
+ */
+ cpup = phba->sli4_hba.cpu_map;
+ cpu = first_cpu;
+ cpup += cpu;
+ for (idx = 0; idx < phba->sli4_hba.num_present_cpu;
+ idx++) {
+ if (cpup->phys_id == i) {
+ /*
+ * Save any IO channels that are
+ * already mapped to this phys_id.
+ */
+ if (cpup->irq != LPFC_VECTOR_MAP_EMPTY) {
+ chann[saved_chann] =
+ cpup->channel_id;
+ saved_chann++;
+ goto out;
+ }
+
+ /* See if we are using round-robin */
+ if (saved_chann == 0)
+ saved_chann =
+ phba->cfg_fcp_io_channel;
+
+ /* Associate next IO channel with CPU */
+ cpup->channel_id = chann[used_chann];
+ num_io_channel++;
+ used_chann++;
+ if (used_chann == saved_chann)
+ used_chann = 0;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3331 Set IO_CHANN "
+ "CPU %d channel %d\n",
+ idx, cpup->channel_id);
+ }
+out:
+ cpu++;
+ if (cpu >= phba->sli4_hba.num_present_cpu) {
+ cpup = phba->sli4_hba.cpu_map;
+ cpu = 0;
+ } else {
+ cpup++;
+ }
+ }
+ }
+
+ if (phba->sli4_hba.num_online_cpu != phba->sli4_hba.num_present_cpu) {
+ cpup = phba->sli4_hba.cpu_map;
+ for (idx = 0; idx < phba->sli4_hba.num_present_cpu; idx++) {
+ if (cpup->channel_id == LPFC_VECTOR_MAP_EMPTY) {
+ cpup->channel_id = 0;
+ num_io_channel++;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3332 Assign IO_CHANN "
+ "CPU %d channel %d\n",
+ idx, cpup->channel_id);
+ }
+ cpup++;
+ }
+ }
+
+ /* Sanity check */
+ if (num_io_channel != phba->sli4_hba.num_present_cpu)
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3333 Set affinity mismatch:"
+ "%d chann != %d cpus: %d vactors\n",
+ num_io_channel, phba->sli4_hba.num_present_cpu,
+ vectors);
+
+ phba->cfg_fcp_io_sched = LPFC_FCP_SCHED_BY_CPU;
+ return 1;
+}
+
+
+/**
* lpfc_sli4_enable_msix - Enable MSI-X interrupt mode to SLI-4 device
* @phba: pointer to lpfc hba data structure.
*
@@ -8259,9 +8662,7 @@ enable_msix_vectors:
phba->sli4_hba.msix_entries[index].vector,
phba->sli4_hba.msix_entries[index].entry);
- /*
- * Assign MSI-X vectors to interrupt handlers
- */
+ /* Assign MSI-X vectors to interrupt handlers */
for (index = 0; index < vectors; index++) {
memset(&phba->sli4_hba.handler_name[index], 0, 16);
sprintf((char *)&phba->sli4_hba.handler_name[index],
@@ -8289,6 +8690,8 @@ enable_msix_vectors:
phba->cfg_fcp_io_channel, vectors);
phba->cfg_fcp_io_channel = vectors;
}
+
+ lpfc_sli4_set_affinity(phba, vectors);
return rc;
cfg_fail_out:
@@ -9213,15 +9616,15 @@ lpfc_sli_prep_dev_for_reset(struct lpfc_hba *phba)
/* 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_fcp_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);
}
/**
@@ -9966,6 +10369,9 @@ lpfc_sli4_prep_dev_for_reset(struct lpfc_hba *phba)
/* 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_fcp_rings(phba);
+
/* stop all timers */
lpfc_stop_hba_timers(phba);
@@ -9973,9 +10379,6 @@ lpfc_sli4_prep_dev_for_reset(struct lpfc_hba *phba)
lpfc_sli4_disable_intr(phba);
lpfc_sli4_queue_destroy(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);
}
/**
@@ -10535,6 +10938,7 @@ static struct miscdevice lpfc_mgmt_dev = {
static int __init
lpfc_init(void)
{
+ int cpu;
int error = 0;
printk(LPFC_MODULE_DESC "\n");
@@ -10561,6 +10965,11 @@ lpfc_init(void)
return -ENOMEM;
}
}
+
+ /* Initialize in case vector mapping is needed */
+ for (cpu = 0; cpu < LPFC_MAX_CPU; cpu++)
+ lpfc_used_cpu[cpu] = LPFC_VECTOR_MAP_EMPTY;
+
error = pci_register_driver(&lpfc_driver);
if (error) {
fc_release_transport(lpfc_transport_template);
diff --git a/drivers/scsi/lpfc/lpfc_logmsg.h b/drivers/scsi/lpfc/lpfc_logmsg.h
index baf53e6c2bd1..2a4e5d21eab2 100644
--- a/drivers/scsi/lpfc/lpfc_logmsg.h
+++ b/drivers/scsi/lpfc/lpfc_logmsg.h
@@ -37,6 +37,7 @@
#define LOG_EVENT 0x00010000 /* CT,TEMP,DUMP, logging */
#define LOG_FIP 0x00020000 /* FIP events */
#define LOG_FCP_UNDER 0x00040000 /* FCP underruns errors */
+#define LOG_SCSI_CMD 0x00080000 /* ALL SCSI commands */
#define LOG_ALL_MSG 0xffffffff /* LOG all messages */
#define lpfc_printf_vlog(vport, level, mask, fmt, arg...) \
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index a7a9fa468308..41363db7d426 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -2149,18 +2149,21 @@ lpfc_reg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport, dma_addr_t phys)
/* Only FC supports upd bit */
if ((phba->sli4_hba.lnk_info.lnk_tp == LPFC_LNK_TYPE_FC) &&
- (vport->fc_flag & FC_VFI_REGISTERED)) {
+ (vport->fc_flag & FC_VFI_REGISTERED) &&
+ (!phba->fc_topology_changed)) {
bf_set(lpfc_reg_vfi_vp, reg_vfi, 0);
bf_set(lpfc_reg_vfi_upd, 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\n",
+ " vfi:%d, vpi:%d, fc_pname:%x%x fc_flag:x%x"
+ " port_state:x%x topology chg:%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]);
+ reg_vfi->wwn[0], reg_vfi->wwn[1], vport->fc_flag,
+ vport->port_state, phba->fc_topology_changed);
}
/**
diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c
index cd86069a0ba8..812d0cd7c86d 100644
--- a/drivers/scsi/lpfc/lpfc_mem.c
+++ b/drivers/scsi/lpfc/lpfc_mem.c
@@ -64,18 +64,26 @@ 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)
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ /* Calculate alignment */
+ if (phba->cfg_sg_dma_buf_size < SLI4_PAGE_SIZE)
+ i = phba->cfg_sg_dma_buf_size;
+ else
+ i = SLI4_PAGE_SIZE;
+
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,
+ i,
0);
- else
+ } 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;
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index 82f4d3542289..31e9b92f5a9b 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -332,9 +332,11 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
/* 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%x "
+ "x%x x%x x%x\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;
@@ -574,7 +576,7 @@ out:
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;
@@ -631,7 +633,8 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
* If there are other active VLinks present,
* re-instantiate the Vlink using FDISC.
*/
- 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);
@@ -648,7 +651,8 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
!(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);
+ mod_timer(&ndlp->nlp_delayfunc,
+ jiffies + msecs_to_jiffies(1000 * 1));
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_DELAY_TMO;
spin_unlock_irq(shost->host_lock);
@@ -969,7 +973,7 @@ 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);
+ mod_timer(&ndlp->nlp_delayfunc, jiffies + msecs_to_jiffies(1000 * 1));
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_DELAY_TMO;
spin_unlock_irq(shost->host_lock);
@@ -1303,7 +1307,8 @@ lpfc_cmpl_adisc_adisc_issue(struct lpfc_vport *vport,
if ((irsp->ulpStatus) ||
(!lpfc_check_adisc(vport, ndlp, &ap->nodeName, &ap->portName))) {
/* 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);
@@ -1509,7 +1514,8 @@ 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);
+ mod_timer(&ndlp->nlp_delayfunc,
+ jiffies + msecs_to_jiffies(1000 * 1));
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_DELAY_TMO;
spin_unlock_irq(shost->host_lock);
@@ -2145,7 +2151,8 @@ lpfc_rcv_prlo_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
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);
+ mod_timer(&ndlp->nlp_delayfunc,
+ jiffies + msecs_to_jiffies(1000 * 1));
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_DELAY_TMO;
ndlp->nlp_flag &= ~NLP_NPR_ADISC;
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index 74b8710e1e90..8523b278ec9d 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -24,6 +24,8 @@
#include <linux/export.h>
#include <linux/delay.h>
#include <asm/unaligned.h>
+#include <linux/crc-t10dif.h>
+#include <net/checksum.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
@@ -48,7 +50,7 @@
#define LPFC_RESET_WAIT 2
#define LPFC_ABORT_WAIT 2
-int _dump_buf_done;
+int _dump_buf_done = 1;
static char *dif_op_str[] = {
"PROT_NORMAL",
@@ -66,6 +68,10 @@ struct scsi_dif_tuple {
__be32 ref_tag; /* Target LBA or indirect LBA */
};
+#if !defined(SCSI_PROT_GUARD_CHECK) || !defined(SCSI_PROT_REF_CHECK)
+#define scsi_prot_flagged(sc, flg) sc
+#endif
+
static void
lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb);
static void
@@ -534,7 +540,16 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc)
dma_addr_t pdma_phys_fcp_rsp;
dma_addr_t pdma_phys_bpl;
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);
@@ -759,7 +774,7 @@ lpfc_sli4_post_scsi_sgl_list(struct lpfc_hba *phba,
struct list_head *post_sblist, int sb_count)
{
struct lpfc_scsi_buf *psb, *psb_next;
- int status;
+ int status, sgl_size;
int post_cnt = 0, block_cnt = 0, num_posting = 0, num_posted = 0;
dma_addr_t pdma_phys_bpl1;
int last_xritag = NO_XRI;
@@ -771,6 +786,9 @@ lpfc_sli4_post_scsi_sgl_list(struct lpfc_hba *phba,
if (sb_count <= 0)
return -EINVAL;
+ sgl_size = phba->cfg_sg_dma_buf_size -
+ (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp));
+
list_for_each_entry_safe(psb, psb_next, post_sblist, list) {
list_del_init(&psb->list);
block_cnt++;
@@ -803,7 +821,7 @@ lpfc_sli4_post_scsi_sgl_list(struct lpfc_hba *phba,
post_cnt = block_cnt;
} else if (block_cnt == 1) {
/* last single sgl with non-contiguous xri */
- if (phba->cfg_sg_dma_buf_size > SGL_PAGE_SIZE)
+ if (sgl_size > SGL_PAGE_SIZE)
pdma_phys_bpl1 = psb->dma_phys_bpl +
SGL_PAGE_SIZE;
else
@@ -885,9 +903,12 @@ lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *phba)
int num_posted, rc = 0;
/* get all SCSI buffers need to repost to a local list */
- spin_lock_irq(&phba->scsi_buf_list_lock);
- list_splice_init(&phba->lpfc_scsi_buf_list, &post_sblist);
- spin_unlock_irq(&phba->scsi_buf_list_lock);
+ spin_lock_irq(&phba->scsi_buf_list_get_lock);
+ spin_lock_irq(&phba->scsi_buf_list_put_lock);
+ list_splice_init(&phba->lpfc_scsi_buf_list_get, &post_sblist);
+ list_splice(&phba->lpfc_scsi_buf_list_put, &post_sblist);
+ spin_unlock_irq(&phba->scsi_buf_list_put_lock);
+ spin_unlock_irq(&phba->scsi_buf_list_get_lock);
/* post the list of scsi buffer sgls to port if available */
if (!list_empty(&post_sblist)) {
@@ -923,13 +944,22 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc)
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;
+ dma_addr_t pdma_phys_bpl;
uint16_t iotag, lxri = 0;
- int bcnt, num_posted;
+ int bcnt, num_posted, sgl_size;
LIST_HEAD(prep_sblist);
LIST_HEAD(post_sblist);
LIST_HEAD(scsi_sblist);
+ sgl_size = phba->cfg_sg_dma_buf_size -
+ (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp));
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+ "9068 ALLOC %d scsi_bufs: %d (%d + %d + %d)\n",
+ num_to_alloc, phba->cfg_sg_dma_buf_size, sgl_size,
+ (int)sizeof(struct fcp_cmnd),
+ (int)sizeof(struct fcp_rsp));
+
for (bcnt = 0; bcnt < num_to_alloc; bcnt++) {
psb = kzalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL);
if (!psb)
@@ -948,6 +978,15 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc)
}
memset(psb->data, 0, phba->cfg_sg_dma_buf_size);
+ /* Page alignment is CRITICAL, double check to be sure */
+ if (((unsigned long)(psb->data) &
+ (unsigned long)(SLI4_PAGE_SIZE - 1)) != 0) {
+ pci_pool_free(phba->lpfc_scsi_dma_buf_pool,
+ psb->data, psb->dma_handle);
+ kfree(psb);
+ break;
+ }
+
/* Allocate iotag for psb->cur_iocbq. */
iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq);
if (iotag == 0) {
@@ -968,17 +1007,14 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc)
psb->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri];
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_cmnd = (psb->data + sgl_size);
psb->fcp_rsp = (struct fcp_rsp *)((uint8_t *)psb->fcp_cmnd +
sizeof(struct fcp_cmnd));
/* 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_cmd = (psb->dma_handle + sgl_size);
pdma_phys_fcp_rsp = pdma_phys_fcp_cmd + sizeof(struct fcp_cmnd);
/*
@@ -1020,17 +1056,13 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc)
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;
/* add the scsi buffer to a post list */
list_add_tail(&psb->list, &post_sblist);
- spin_lock_irq(&phba->scsi_buf_list_lock);
+ spin_lock_irq(&phba->scsi_buf_list_get_lock);
phba->sli4_hba.scsi_xri_cnt++;
- spin_unlock_irq(&phba->scsi_buf_list_lock);
+ spin_unlock_irq(&phba->scsi_buf_list_get_lock);
}
lpfc_printf_log(phba, KERN_INFO, LOG_BG,
"3021 Allocate %d out of %d requested new SCSI "
@@ -1079,17 +1111,23 @@ static struct lpfc_scsi_buf*
lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
{
struct lpfc_scsi_buf * lpfc_cmd = NULL;
- struct list_head *scsi_buf_list = &phba->lpfc_scsi_buf_list;
- 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;
+ struct list_head *scsi_buf_list_get = &phba->lpfc_scsi_buf_list_get;
+ unsigned long gflag = 0;
+ unsigned long pflag = 0;
+
+ spin_lock_irqsave(&phba->scsi_buf_list_get_lock, gflag);
+ list_remove_head(scsi_buf_list_get, lpfc_cmd, struct lpfc_scsi_buf,
+ list);
+ if (!lpfc_cmd) {
+ spin_lock_irqsave(&phba->scsi_buf_list_put_lock, pflag);
+ 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_scsi_buf, list);
+ spin_unlock_irqrestore(&phba->scsi_buf_list_put_lock, pflag);
}
- spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag);
+ spin_unlock_irqrestore(&phba->scsi_buf_list_get_lock, gflag);
return lpfc_cmd;
}
/**
@@ -1107,28 +1145,39 @@ static struct lpfc_scsi_buf*
lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
{
struct lpfc_scsi_buf *lpfc_cmd ;
- unsigned long iflag = 0;
+ unsigned long gflag = 0;
+ unsigned long pflag = 0;
int found = 0;
- spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag);
- list_for_each_entry(lpfc_cmd, &phba->lpfc_scsi_buf_list,
- list) {
+ spin_lock_irqsave(&phba->scsi_buf_list_get_lock, gflag);
+ list_for_each_entry(lpfc_cmd, &phba->lpfc_scsi_buf_list_get, list) {
if (lpfc_test_rrq_active(phba, ndlp,
lpfc_cmd->cur_iocbq.sli4_lxritag))
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;
}
- spin_unlock_irqrestore(&phba->scsi_buf_list_lock,
- iflag);
+ if (!found) {
+ spin_lock_irqsave(&phba->scsi_buf_list_put_lock, pflag);
+ list_splice(&phba->lpfc_scsi_buf_list_put,
+ &phba->lpfc_scsi_buf_list_get);
+ INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put);
+ spin_unlock_irqrestore(&phba->scsi_buf_list_put_lock, pflag);
+ list_for_each_entry(lpfc_cmd, &phba->lpfc_scsi_buf_list_get,
+ list) {
+ if (lpfc_test_rrq_active(
+ phba, ndlp, lpfc_cmd->cur_iocbq.sli4_lxritag))
+ continue;
+ list_del(&lpfc_cmd->list);
+ found = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&phba->scsi_buf_list_get_lock, gflag);
if (!found)
return NULL;
- else
- return lpfc_cmd;
+ return lpfc_cmd;
}
/**
* lpfc_get_scsi_buf - Get a scsi buffer from lpfc_scsi_buf_list of the HBA
@@ -1160,10 +1209,15 @@ lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
{
unsigned long iflag = 0;
- spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag);
+ psb->seg_cnt = 0;
+ psb->nonsg_phys = 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.iocb_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);
}
/**
@@ -1181,6 +1235,10 @@ lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
{
unsigned long iflag = 0;
+ psb->seg_cnt = 0;
+ psb->nonsg_phys = 0;
+ psb->prot_seg_cnt = 0;
+
if (psb->exch_busy) {
spin_lock_irqsave(&phba->sli4_hba.abts_scsi_buf_list_lock,
iflag);
@@ -1190,11 +1248,11 @@ lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
spin_unlock_irqrestore(&phba->sli4_hba.abts_scsi_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);
+ psb->cur_iocbq.iocb_flag = LPFC_IO_FCP;
+ spin_lock_irqsave(&phba->scsi_buf_list_put_lock, iflag);
+ list_add_tail(&psb->list, &phba->lpfc_scsi_buf_list_put);
+ spin_unlock_irqrestore(&phba->scsi_buf_list_put_lock, iflag);
}
}
@@ -1268,6 +1326,7 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
"dma_map_sg. Config %d, seg_cnt %d\n",
__func__, phba->cfg_sg_seg_cnt,
lpfc_cmd->seg_cnt);
+ lpfc_cmd->seg_cnt = 0;
scsi_dma_unmap(scsi_cmnd);
return 1;
}
@@ -2013,9 +2072,21 @@ 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, checking);
- bf_set(pde6_re, pde6, checking);
+ if (scsi_prot_flagged(sc, SCSI_PROT_GUARD_CHECK))
+ bf_set(pde6_ce, pde6, checking);
+ else
+ bf_set(pde6_ce, pde6, 0);
+
+ if (scsi_prot_flagged(sc, 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_ae, pde6, 0);
@@ -2145,6 +2216,10 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
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));
@@ -2164,8 +2239,17 @@ 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, checking);
- bf_set(pde6_re, pde6, checking);
+
+ if (scsi_prot_flagged(sc, SCSI_PROT_GUARD_CHECK))
+ bf_set(pde6_ce, pde6, checking);
+ else
+ bf_set(pde6_ce, pde6, 0);
+
+ if (scsi_prot_flagged(sc, 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_ae, pde6, 0);
bf_set(pde6_apptagval, pde6, 0);
@@ -2213,6 +2297,10 @@ 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,
"9065 BLKGRD:%s Invalid data segment\n",
@@ -2324,7 +2412,6 @@ lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
struct sli4_sge_diseed *diseed = NULL;
dma_addr_t physaddr;
int i = 0, num_sge = 0, status;
- int datadir = sc->sc_data_direction;
uint32_t reftag;
unsigned blksize;
uint8_t txop, rxop;
@@ -2362,13 +2449,26 @@ lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
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 (scsi_prot_flagged(sc, SCSI_PROT_GUARD_CHECK))
+ bf_set(lpfc_sli4_sge_dif_ce, diseed, checking);
+ else
+ bf_set(lpfc_sli4_sge_dif_ce, diseed, 0);
+
+ if (scsi_prot_flagged(sc, 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);
- if (datadir == DMA_FROM_DEVICE) {
- bf_set(lpfc_sli4_sge_dif_ce, diseed, checking);
- bf_set(lpfc_sli4_sge_dif_re, diseed, checking);
- }
+
bf_set(lpfc_sli4_sge_dif_ai, diseed, 1);
bf_set(lpfc_sli4_sge_dif_me, diseed, 0);
@@ -2497,6 +2597,10 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
split_offset = 0;
do {
+ /* Check to see if we ran out of space */
+ if (num_sge >= (phba->cfg_total_seg_cnt - 2))
+ return num_sge + 3;
+
/* setup DISEED with what we have */
diseed = (struct sli4_sge_diseed *) sgl;
memset(diseed, 0, sizeof(struct sli4_sge_diseed));
@@ -2506,11 +2610,34 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
diseed->ref_tag = cpu_to_le32(reftag);
diseed->ref_tag_tran = diseed->ref_tag;
+ if (scsi_prot_flagged(sc, 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 (scsi_prot_flagged(sc, 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_ce, diseed, checking);
- bf_set(lpfc_sli4_sge_dif_re, diseed, checking);
+
bf_set(lpfc_sli4_sge_dif_ai, diseed, 1);
bf_set(lpfc_sli4_sge_dif_me, diseed, 0);
@@ -2556,6 +2683,10 @@ lpfc_bg_setup_sgl_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_sge >= phba->cfg_total_seg_cnt)
+ return num_sge + 1;
+
if (!sgde) {
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
"9086 BLKGRD:%s Invalid data segment\n",
@@ -2670,6 +2801,47 @@ lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc)
}
/**
+ * 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_scsi_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 */
+ if (scsi_get_prot_op(sc) == SCSI_PROT_READ_INSERT)
+ return fcpdl;
+
+ } else {
+ /* Write */
+ 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.
+ */
+ if (scsi_prot_flagged(sc, SCSI_PROT_TRANSFER_PI))
+ fcpdl += (fcpdl / lpfc_cmd_blksize(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.
@@ -2689,8 +2861,7 @@ lpfc_bg_scsi_prep_dma_buf_s3(struct lpfc_hba *phba,
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;
/*
* Start the lpfc command prep by bumping the bpl beyond fcp_cmnd
@@ -2711,28 +2882,28 @@ lpfc_bg_scsi_prep_dma_buf_s3(struct lpfc_hba *phba,
return 1;
lpfc_cmd->seg_cnt = datasegcnt;
- 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;
- }
+
+ /* First check if data segment count from SCSI Layer is good */
+ if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt)
+ 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)
+ 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)
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
@@ -2747,31 +2918,28 @@ lpfc_bg_scsi_prep_dma_buf_s3(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))
+ 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))
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_FCP,
"9022 Unexpected protection group %i\n",
prot_group_type);
@@ -2790,18 +2958,7 @@ lpfc_bg_scsi_prep_dma_buf_s3(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;
- }
+ fcpdl = lpfc_bg_scsi_adjust_dl(phba, lpfc_cmd);
fcp_cmnd->fcpDl = be32_to_cpu(fcpdl);
/*
@@ -2812,14 +2969,234 @@ lpfc_bg_scsi_prep_dma_buf_s3(struct lpfc_hba *phba,
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_FCP,
- "9023 Could not setup all needed BDE's"
- "prot_group_type=%d, num_bde=%d\n",
+ "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);
+
+ lpfc_cmd->seg_cnt = 0;
+ lpfc_cmd->prot_seg_cnt = 0;
return 1;
}
/*
+ * This function calcuates the T10 DIF guard tag
+ * on the specified data using a CRC algorithmn
+ * using crc_t10dif.
+ */
+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.
+ */
+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.
+ */
+void
+lpfc_calc_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_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, guard_type;
+ 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 = lpfc_cmd_blksize(cmd);
+ data_src = (uint8_t *)sg_virt(sgde);
+ data_len = sgde->length;
+ if ((data_len & (blksize - 1)) == 0)
+ chk_guard = 1;
+ guard_type = scsi_host_get_guard(cmd->device->host);
+
+ start_ref_tag = (uint32_t)scsi_get_lba(cmd); /* Truncate LBA */
+ start_app_tag = src->app_tag;
+ src = (struct scsi_dif_tuple *)sg_virt(sgpe);
+ len = sgpe->length;
+ while (src && protsegcnt) {
+ while (len) {
+
+ /*
+ * First check to see if a protection data
+ * check is valid
+ */
+ if ((src->ref_tag == 0xffffffff) ||
+ (src->app_tag == 0xffff)) {
+ start_ref_tag++;
+ goto skipit;
+ }
+
+ /* App Tag checking */
+ app_tag = src->app_tag;
+ if (chk_app && (app_tag != start_app_tag)) {
+ err_type = BGS_APPTAG_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++;
+
+ /* Guard Tag checking */
+ if (chk_guard) {
+ guard_tag = src->guard_tag;
+ if (guard_type == SHOST_DIX_GUARD_IP)
+ 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;
+ }
+ }
+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 = sgde->length;
+ 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 = sgpe->length;
+ } else {
+ src = NULL;
+ }
+ protsegcnt--;
+ }
+ }
+out:
+ if (err_type == BGS_GUARD_ERR_MASK) {
+ scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, 0x1);
+ cmd->result = DRIVER_SENSE << 24
+ | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
+ phba->bg_guard_err_cnt++;
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP | LOG_BG,
+ "9069 BLKGRD: LBA %lx grd_tag error %x != %x\n",
+ (unsigned long)scsi_get_lba(cmd),
+ sum, guard_tag);
+
+ } else if (err_type == BGS_REFTAG_ERR_MASK) {
+ scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, 0x3);
+ cmd->result = DRIVER_SENSE << 24
+ | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
+
+ phba->bg_reftag_err_cnt++;
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP | LOG_BG,
+ "9066 BLKGRD: LBA %lx ref_tag error %x != %x\n",
+ (unsigned long)scsi_get_lba(cmd),
+ ref_tag, start_ref_tag);
+
+ } else if (err_type == BGS_APPTAG_ERR_MASK) {
+ scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, 0x2);
+ cmd->result = DRIVER_SENSE << 24
+ | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
+
+ phba->bg_apptag_err_cnt++;
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP | LOG_BG,
+ "9041 BLKGRD: LBA %lx app_tag error %x != %x\n",
+ (unsigned long)scsi_get_lba(cmd),
+ app_tag, start_app_tag);
+ }
+}
+
+
+/*
* This function checks for BlockGuard errors detected by
* the HBA. In case of errors, the ASC/ASCQ fields in the
* sense buffer will be set accordingly, paired with
@@ -2842,12 +3219,6 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
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"
@@ -2870,18 +3241,24 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
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);
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP | LOG_BG,
+ "9072 BLKGRD: Invalid BG Profile in cmd"
+ " 0x%x lba 0x%llx blk cnt 0x%x "
+ "bgstat=x%x bghm=x%x\n", cmd->cmnd[0],
+ (unsigned long long)scsi_get_lba(cmd),
+ blk_rq_sectors(cmd->request), 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);
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP | LOG_BG,
+ "9073 BLKGRD: Invalid BG PDIF Block in cmd"
+ " 0x%x lba 0x%llx blk cnt 0x%x "
+ "bgstat=x%x bghm=x%x\n", cmd->cmnd[0],
+ (unsigned long long)scsi_get_lba(cmd),
+ blk_rq_sectors(cmd->request), bgstat, bghm);
ret = (-1);
goto out;
}
@@ -2894,8 +3271,12 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
cmd->result = DRIVER_SENSE << 24
| ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
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 lba 0x%llx blk cnt 0x%x "
+ "bgstat=x%x bghm=x%x\n", cmd->cmnd[0],
+ (unsigned long long)scsi_get_lba(cmd),
+ blk_rq_sectors(cmd->request), bgstat, bghm);
}
if (lpfc_bgs_get_reftag_err(bgstat)) {
@@ -2907,8 +3288,12 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
| ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
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 lba 0x%llx blk cnt 0x%x "
+ "bgstat=x%x bghm=x%x\n", cmd->cmnd[0],
+ (unsigned long long)scsi_get_lba(cmd),
+ blk_rq_sectors(cmd->request), bgstat, bghm);
}
if (lpfc_bgs_get_apptag_err(bgstat)) {
@@ -2920,8 +3305,12 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
| ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
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 lba 0x%llx blk cnt 0x%x "
+ "bgstat=x%x bghm=x%x\n", cmd->cmnd[0],
+ (unsigned long long)scsi_get_lba(cmd),
+ blk_rq_sectors(cmd->request), bgstat, bghm);
}
if (lpfc_bgs_get_hi_water_mark_present(bgstat)) {
@@ -2960,11 +3349,16 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
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: Unknown error reported!\n");
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP | LOG_BG,
+ "9057 BLKGRD: Unknown error in cmd"
+ " 0x%x lba 0x%llx blk cnt 0x%x "
+ "bgstat=x%x bghm=x%x\n", cmd->cmnd[0],
+ (unsigned long long)scsi_get_lba(cmd),
+ blk_rq_sectors(cmd->request), bgstat, bghm);
+
+ /* Calcuate what type of error it was */
+ lpfc_calc_bg_err(phba, lpfc_cmd);
}
-
out:
return ret;
}
@@ -3028,6 +3422,7 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
"dma_map_sg. Config %d, seg_cnt %d\n",
__func__, phba->cfg_sg_seg_cnt,
lpfc_cmd->seg_cnt);
+ lpfc_cmd->seg_cnt = 0;
scsi_dma_unmap(scsi_cmnd);
return 1;
}
@@ -3094,45 +3489,6 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
}
/**
- * 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_scsi_buf *lpfc_cmd)
-{
- struct scsi_cmnd *sc = lpfc_cmd->pCmd;
- int diflen, fcpdl;
- unsigned blksize;
-
- fcpdl = scsi_bufflen(sc);
-
- /* Check if there is protection data on the wire */
- if (sc->sc_data_direction == DMA_FROM_DEVICE) {
- /* Read */
- if (scsi_get_prot_op(sc) == SCSI_PROT_READ_INSERT)
- return fcpdl;
-
- } else {
- /* Write */
- if (scsi_get_prot_op(sc) == SCSI_PROT_WRITE_STRIP)
- return fcpdl;
- }
-
- /* If protection data on the wire, adjust the count accordingly */
- blksize = lpfc_cmd_blksize(sc);
- diflen = (fcpdl / blksize) * 8;
- fcpdl += diflen;
- return fcpdl;
-}
-
-/**
* 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.
@@ -3149,14 +3505,14 @@ lpfc_bg_scsi_prep_dma_buf_s4(struct lpfc_hba *phba,
struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
struct sli4_sge *sgl = (struct sli4_sge *)(lpfc_cmd->fcp_bpl);
IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
- uint32_t num_bde = 0;
+ uint32_t num_sge = 0;
int datasegcnt, protsegcnt, datadir = scsi_cmnd->sc_data_direction;
int prot_group_type = 0;
int fcpdl;
/*
* Start the lpfc command prep by bumping the sgl beyond fcp_cmnd
- * fcp_rsp regions to the first data bde entry
+ * fcp_rsp regions to the first data sge entry
*/
if (scsi_sg_count(scsi_cmnd)) {
/*
@@ -3179,28 +3535,28 @@ lpfc_bg_scsi_prep_dma_buf_s4(struct lpfc_hba *phba,
sgl += 1;
lpfc_cmd->seg_cnt = datasegcnt;
- if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9087 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;
- }
+
+ /* First check if data segment count from SCSI Layer is good */
+ if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt)
+ goto err;
prot_group_type = lpfc_prot_group_type(phba, scsi_cmnd);
switch (prot_group_type) {
case LPFC_PG_TYPE_NO_DIF:
- num_bde = lpfc_bg_setup_sgl(phba, scsi_cmnd, sgl,
+ /* Here we need to add a DISEED to the count */
+ if ((lpfc_cmd->seg_cnt + 1) > phba->cfg_total_seg_cnt)
+ goto err;
+
+ num_sge = lpfc_bg_setup_sgl(phba, scsi_cmnd, sgl,
datasegcnt);
+
/* we should have 2 or more entries in buffer list */
- if (num_bde < 2)
+ if (num_sge < 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
@@ -3215,31 +3571,28 @@ lpfc_bg_scsi_prep_dma_buf_s4(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,
- "9088 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 3 SGEs used for every
+ * protection data segment.
+ */
+ if ((lpfc_cmd->prot_seg_cnt * 3) >
+ (phba->cfg_total_seg_cnt - 2))
+ goto err;
- num_bde = lpfc_bg_setup_sgl_prot(phba, scsi_cmnd, sgl,
+ num_sge = lpfc_bg_setup_sgl_prot(phba, scsi_cmnd, sgl,
datasegcnt, protsegcnt);
+
/* we should have 3 or more entries in buffer list */
- if (num_bde < 3)
+ if ((num_sge < 3) ||
+ (num_sge > phba->cfg_total_seg_cnt))
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_FCP,
"9083 Unexpected protection group %i\n",
prot_group_type);
@@ -3263,7 +3616,6 @@ lpfc_bg_scsi_prep_dma_buf_s4(struct lpfc_hba *phba,
}
fcpdl = lpfc_bg_scsi_adjust_dl(phba, lpfc_cmd);
-
fcp_cmnd->fcpDl = be32_to_cpu(fcpdl);
/*
@@ -3274,10 +3626,22 @@ lpfc_bg_scsi_prep_dma_buf_s4(struct lpfc_hba *phba,
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_FCP,
- "9084 Could not setup all needed BDE's"
- "prot_group_type=%d, num_bde=%d\n",
- prot_group_type, num_bde);
+ "9084 Cannot setup S/G List for HBA"
+ "IO segs %d/%d SGL %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_sge);
+
+ lpfc_cmd->seg_cnt = 0;
+ lpfc_cmd->prot_seg_cnt = 0;
return 1;
}
@@ -4357,7 +4721,8 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
if (scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
if (vport->phba->cfg_enable_bg) {
- lpfc_printf_vlog(vport, KERN_INFO, LOG_BG,
+ lpfc_printf_vlog(vport,
+ KERN_INFO, LOG_SCSI_CMD,
"9033 BLKGRD: rcvd %s cmd:x%x "
"sector x%llx cnt %u pt %x\n",
dif_op_str[scsi_get_prot_op(cmnd)],
@@ -4369,7 +4734,8 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
err = lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd);
} else {
if (vport->phba->cfg_enable_bg) {
- lpfc_printf_vlog(vport, KERN_INFO, LOG_BG,
+ lpfc_printf_vlog(vport,
+ KERN_INFO, LOG_SCSI_CMD,
"9038 BLKGRD: rcvd PROT_NORMAL cmd: "
"x%x sector x%llx cnt %u pt %x\n",
cmnd->cmnd[0],
@@ -4542,7 +4908,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
/* Wait for abort to complete */
wait_event_timeout(waitq,
(lpfc_cmd->pCmd != cmnd),
- (2*vport->cfg_devloss_tmo*HZ));
+ msecs_to_jiffies(2*vport->cfg_devloss_tmo*1000));
lpfc_cmd->waitq = NULL;
if (lpfc_cmd->pCmd == cmnd) {
@@ -5012,16 +5378,24 @@ lpfc_host_reset_handler(struct scsi_cmnd *cmnd)
struct lpfc_hba *phba = vport->phba;
int rc, ret = SUCCESS;
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+ "3172 SCSI layer issued Host Reset Data:\n");
+
lpfc_offline_prep(phba, LPFC_MBX_WAIT);
lpfc_offline(phba);
rc = lpfc_sli_brdrestart(phba);
if (rc)
ret = FAILED;
- lpfc_online(phba);
+ rc = lpfc_online(phba);
+ if (rc)
+ ret = FAILED;
lpfc_unblock_mgmt_io(phba);
- lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
- "3172 SCSI layer issued Host Reset Data: x%x\n", ret);
+ if (ret == FAILED) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+ "3323 Failed host reset, bring it offline\n");
+ lpfc_sli4_offline_eratt(phba);
+ }
return ret;
}
@@ -5088,11 +5462,11 @@ lpfc_slave_alloc(struct scsi_device *sdev)
}
num_allocated = lpfc_new_scsi_buf(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_FCP,
+ "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;
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 35dd17eb0f27..572579f87de4 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -667,7 +667,7 @@ lpfc_handle_rrq_active(struct lpfc_hba *phba)
spin_lock_irqsave(&phba->hbalock, iflags);
phba->hba_flag &= ~HBA_RRQ_ACTIVE;
- next_time = jiffies + HZ * (phba->fc_ratov + 1);
+ next_time = jiffies + msecs_to_jiffies(1000 * (phba->fc_ratov + 1));
list_for_each_entry_safe(rrq, nextrrq,
&phba->active_rrq_list, list) {
if (time_after(jiffies, rrq->rrq_stop_time))
@@ -782,7 +782,7 @@ lpfc_cleanup_wt_rrqs(struct lpfc_hba *phba)
return;
spin_lock_irqsave(&phba->hbalock, iflags);
phba->hba_flag &= ~HBA_RRQ_ACTIVE;
- next_time = jiffies + HZ * (phba->fc_ratov * 2);
+ next_time = jiffies + msecs_to_jiffies(1000 * (phba->fc_ratov * 2));
list_splice_init(&phba->active_rrq_list, &rrq_list);
spin_unlock_irqrestore(&phba->hbalock, iflags);
@@ -878,7 +878,8 @@ lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
else
rrq->send_rrq = 0;
rrq->xritag = xritag;
- rrq->rrq_stop_time = jiffies + HZ * (phba->fc_ratov + 1);
+ rrq->rrq_stop_time = jiffies +
+ msecs_to_jiffies(1000 * (phba->fc_ratov + 1));
rrq->ndlp = ndlp;
rrq->nlp_DID = ndlp->nlp_DID;
rrq->vport = ndlp->vport;
@@ -926,8 +927,7 @@ __lpfc_sli_get_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq)
} else if ((piocbq->iocb.ulpCommand == CMD_GEN_REQUEST64_CR) &&
!(piocbq->iocb_flag & LPFC_IO_LIBDFC))
ndlp = piocbq->context_un.ndlp;
- else if ((piocbq->iocb.ulpCommand == CMD_ELS_REQUEST64_CR) &&
- (piocbq->iocb_flag & LPFC_IO_LIBDFC))
+ else if (piocbq->iocb_flag & LPFC_IO_LIBDFC)
ndlp = piocbq->context_un.ndlp;
else
ndlp = piocbq->context1;
@@ -1339,7 +1339,8 @@ lpfc_sli_ringtxcmpl_put(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
BUG();
else
mod_timer(&piocb->vport->els_tmofunc,
- jiffies + HZ * (phba->fc_ratov << 1));
+ jiffies +
+ msecs_to_jiffies(1000 * (phba->fc_ratov << 1)));
}
@@ -2340,7 +2341,8 @@ 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/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",
+ "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_sli_config_mbox_subsys_get(phba, pmb),
@@ -2354,7 +2356,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);
@@ -2908,8 +2913,9 @@ void lpfc_poll_eratt(unsigned long ptr)
lpfc_worker_wake_up(phba);
else
/* Restart the timer for next eratt poll */
- mod_timer(&phba->eratt_poll, jiffies +
- HZ * LPFC_ERATT_POLL_INTERVAL);
+ mod_timer(&phba->eratt_poll,
+ jiffies +
+ msecs_to_jiffies(1000 * LPFC_ERATT_POLL_INTERVAL));
return;
}
@@ -5511,6 +5517,7 @@ 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);
@@ -5811,6 +5818,7 @@ 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);
@@ -5992,7 +6000,7 @@ lpfc_sli4_repost_els_sgl_list(struct lpfc_hba *phba)
struct lpfc_sglq *sglq_entry = NULL;
struct lpfc_sglq *sglq_entry_next = NULL;
struct lpfc_sglq *sglq_entry_first = NULL;
- int status, post_cnt = 0, num_posted = 0, block_cnt = 0;
+ int status, total_cnt, post_cnt = 0, num_posted = 0, block_cnt = 0;
int last_xritag = NO_XRI;
LIST_HEAD(prep_sgl_list);
LIST_HEAD(blck_sgl_list);
@@ -6004,6 +6012,7 @@ lpfc_sli4_repost_els_sgl_list(struct lpfc_hba *phba)
list_splice_init(&phba->sli4_hba.lpfc_sgl_list, &allc_sgl_list);
spin_unlock_irq(&phba->hbalock);
+ total_cnt = phba->sli4_hba.els_xri_cnt;
list_for_each_entry_safe(sglq_entry, sglq_entry_next,
&allc_sgl_list, list) {
list_del_init(&sglq_entry->list);
@@ -6055,9 +6064,7 @@ lpfc_sli4_repost_els_sgl_list(struct lpfc_hba *phba)
sglq_entry->sli4_xritag);
list_add_tail(&sglq_entry->list,
&free_sgl_list);
- spin_lock_irq(&phba->hbalock);
- phba->sli4_hba.els_xri_cnt--;
- spin_unlock_irq(&phba->hbalock);
+ total_cnt--;
}
}
}
@@ -6085,9 +6092,7 @@ lpfc_sli4_repost_els_sgl_list(struct lpfc_hba *phba)
(sglq_entry_first->sli4_xritag +
post_cnt - 1));
list_splice_init(&blck_sgl_list, &free_sgl_list);
- spin_lock_irq(&phba->hbalock);
- phba->sli4_hba.els_xri_cnt -= post_cnt;
- spin_unlock_irq(&phba->hbalock);
+ total_cnt -= post_cnt;
}
/* don't reset xirtag due to hole in xri block */
@@ -6097,6 +6102,8 @@ lpfc_sli4_repost_els_sgl_list(struct lpfc_hba *phba)
/* reset els sgl post count for next round of posting */
post_cnt = 0;
}
+ /* update the number of XRIs posted for ELS */
+ phba->sli4_hba.els_xri_cnt = total_cnt;
/* free the els sgls failed to post */
lpfc_free_sgl_list(phba, &free_sgl_list);
@@ -6446,16 +6453,17 @@ 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 + msecs_to_jiffies(1000 * (phba->fc_ratov * 2)));
/* Start heart beat timer */
mod_timer(&phba->hb_tmofunc,
- jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
+ jiffies + msecs_to_jiffies(1000 * LPFC_HB_MBOX_INTERVAL));
phba->hb_outstanding = 0;
phba->last_completion_time = jiffies;
/* Start error attention (ERATT) polling timer */
- mod_timer(&phba->eratt_poll, jiffies + HZ * LPFC_ERATT_POLL_INTERVAL);
+ mod_timer(&phba->eratt_poll,
+ jiffies + msecs_to_jiffies(1000 * LPFC_ERATT_POLL_INTERVAL));
/* Enable PCIe device Advanced Error Reporting (AER) if configured */
if (phba->cfg_aer_support == 1 && !(phba->hba_flag & HBA_AER_ENABLED)) {
@@ -6822,8 +6830,9 @@ 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, pmbox))));
+ timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba, pmbox) *
+ 1000);
+ mod_timer(&psli->mbox_tmo, jiffies + timeout);
}
/* Mailbox cmd <cmd> issue */
@@ -7496,7 +7505,7 @@ 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, mboxq))));
+ msecs_to_jiffies(1000 * lpfc_mbox_tmo_val(phba, mboxq))));
lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
"(%d):0355 Mailbox cmd x%x (x%x/x%x) issue Data: "
@@ -7914,15 +7923,21 @@ lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq,
static inline uint32_t
lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba)
{
- int i;
-
- if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_CPU)
- i = smp_processor_id();
- else
- i = atomic_add_return(1, &phba->fcp_qidx);
+ struct lpfc_vector_map_info *cpup;
+ int chann, cpu;
- i = (i % phba->cfg_fcp_io_channel);
- return i;
+ if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_CPU) {
+ cpu = smp_processor_id();
+ if (cpu < phba->sli4_hba.num_present_cpu) {
+ cpup = phba->sli4_hba.cpu_map;
+ cpup += cpu;
+ return cpup->channel_id;
+ }
+ chann = cpu;
+ }
+ chann = atomic_add_return(1, &phba->fcp_qidx);
+ chann = (chann % phba->cfg_fcp_io_channel);
+ return chann;
}
/**
@@ -8444,10 +8459,14 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
if ((piocb->iocb_flag & LPFC_IO_FCP) ||
(piocb->iocb_flag & LPFC_USE_FCPWQIDX)) {
+ if (unlikely(!phba->sli4_hba.fcp_wq))
+ return IOCB_ERROR;
if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[piocb->fcp_wqidx],
&wqe))
return IOCB_ERROR;
} else {
+ if (unlikely(!phba->sli4_hba.els_wq))
+ return IOCB_ERROR;
if (lpfc_sli4_wq_put(phba->sli4_hba.els_wq, &wqe))
return IOCB_ERROR;
}
@@ -10003,7 +10022,7 @@ 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 = msecs_to_jiffies(timeout * 1000);
timeleft = wait_event_timeout(done_q,
lpfc_chk_iocb_flg(phba, piocb, LPFC_IO_WAKE),
timeout_req);
@@ -10108,7 +10127,7 @@ lpfc_sli_issue_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq,
if (retval == MBX_BUSY || retval == MBX_SUCCESS) {
wait_event_interruptible_timeout(done_q,
pmboxq->mbox_flag & LPFC_MBX_WAKE,
- timeout * HZ);
+ msecs_to_jiffies(timeout * 1000));
spin_lock_irqsave(&phba->hbalock, flag);
pmboxq->context1 = NULL;
@@ -12899,8 +12918,9 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
}
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\n",
- wq->queue_id, pci_barset, db_offset);
+ "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_format = LPFC_DB_LIST_FORMAT;
wq->db_regaddr = phba->sli4_hba.WQDBregaddr;
@@ -13120,8 +13140,9 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
}
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\n",
- hrq->queue_id, pci_barset, db_offset);
+ "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;
@@ -13971,13 +13992,14 @@ lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr)
}
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:%s (x%x), type:%s (x%x), "
+ "frame Data:%08x %08x %08x %08x %08x %08x %08x\n",
+ rctl_names[fc_hdr->fh_r_ctl], fc_hdr->fh_r_ctl,
+ type_names[fc_hdr->fh_type], 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,
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
index be02b59ea279..67af460184ba 100644
--- a/drivers/scsi/lpfc/lpfc_sli4.h
+++ b/drivers/scsi/lpfc/lpfc_sli4.h
@@ -346,11 +346,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
*/
@@ -440,6 +435,17 @@ struct lpfc_sli4_lnk_info {
#define LPFC_SLI4_HANDLER_NAME_SZ 16
+/* Used for IRQ vector to CPU mapping */
+struct lpfc_vector_map_info {
+ uint16_t phys_id;
+ uint16_t core_id;
+ uint16_t irq;
+ uint16_t channel_id;
+ struct cpumask maskbits;
+};
+#define LPFC_VECTOR_MAP_EMPTY 0xffff
+#define LPFC_MAX_CPU 256
+
/* SLI4 HBA data structure entries */
struct lpfc_sli4_hba {
void __iomem *conf_regs_memmap_p; /* Kernel memory mapped address for
@@ -573,6 +579,11 @@ struct lpfc_sli4_hba {
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 */
+
+ /* CPU to vector mapping information */
+ struct lpfc_vector_map_info *cpu_map;
+ uint16_t num_online_cpu;
+ uint16_t num_present_cpu;
};
enum lpfc_sge_type {
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index 664cd04f7cd8..a38dc3b16969 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -18,7 +18,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "8.3.38"
+#define LPFC_DRIVER_VERSION "8.3.39"
#define LPFC_DRIVER_NAME "lpfc"
/* Used for SLI 2/3 */
diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c
index 0fe188e66000..e28e431564b0 100644
--- a/drivers/scsi/lpfc/lpfc_vport.c
+++ b/drivers/scsi/lpfc/lpfc_vport.c
@@ -80,7 +80,7 @@ inline void lpfc_vport_set_state(struct lpfc_vport *vport,
}
}
-static int
+int
lpfc_alloc_vpi(struct lpfc_hba *phba)
{
unsigned long vpi;
@@ -568,6 +568,7 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
struct lpfc_vport *vport = *(struct lpfc_vport **)fc_vport->dd_data;
struct lpfc_hba *phba = vport->phba;
long timeout;
+ bool ns_ndlp_referenced = false;
if (vport->port_type == LPFC_PHYSICAL_PORT) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
@@ -628,6 +629,18 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
lpfc_debugfs_terminate(vport);
+ /*
+ * The call to fc_remove_host might release the NameServer ndlp. Since
+ * we might need to use the ndlp to send the DA_ID CT command,
+ * increment the reference for the NameServer ndlp to prevent it from
+ * being released.
+ */
+ ndlp = lpfc_findnode_did(vport, NameServer_DID);
+ if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
+ lpfc_nlp_get(ndlp);
+ ns_ndlp_referenced = true;
+ }
+
/* 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));
@@ -734,6 +747,16 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
lpfc_discovery_wait(vport);
skip_logo:
+
+ /*
+ * If the NameServer ndlp has been incremented to allow the DA_ID CT
+ * command to be sent, decrement the ndlp now.
+ */
+ if (ns_ndlp_referenced) {
+ ndlp = lpfc_findnode_did(vport, NameServer_DID);
+ lpfc_nlp_put(ndlp);
+ }
+
lpfc_cleanup(vport);
lpfc_sli_host_down(vport);
diff --git a/drivers/scsi/lpfc/lpfc_vport.h b/drivers/scsi/lpfc/lpfc_vport.h
index 90828340acea..6b2c94eb8134 100644
--- a/drivers/scsi/lpfc/lpfc_vport.h
+++ b/drivers/scsi/lpfc/lpfc_vport.h
@@ -90,6 +90,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.
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index 7c90d57b867e..3a9ddae86f1f 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -4931,11 +4931,12 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg)
printk(KERN_ERR "megaraid_sas: timed out while"
"waiting for HBA to recover\n");
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:
diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c
index 74550922ad55..7b7381d7671f 100644
--- a/drivers/scsi/mvsas/mv_init.c
+++ b/drivers/scsi/mvsas/mv_init.c
@@ -254,7 +254,7 @@ static int 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);
diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c
index 532110f4562a..c9e244984e30 100644
--- a/drivers/scsi/mvsas/mv_sas.c
+++ b/drivers/scsi/mvsas/mv_sas.c
@@ -706,7 +706,7 @@ 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)
{
@@ -726,7 +726,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;
}
@@ -1159,10 +1159,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)
@@ -1260,7 +1260,7 @@ 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];
}
@@ -1278,7 +1278,7 @@ 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;
}
@@ -1480,7 +1480,7 @@ static int mvs_debug_I_T_nexus_reset(struct domain_device *dev)
{
int rc;
struct sas_phy *phy = sas_get_local_phy(dev);
- int reset_type = (dev->dev_type == SATA_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);
@@ -1629,7 +1629,7 @@ int mvs_abort_task(struct sas_task *task)
} else if (task->task_proto & SAS_PROTOCOL_SATA ||
task->task_proto & SAS_PROTOCOL_STP) {
- if (SATA_DEV == dev->dev_type) {
+ if (SAS_SATA_DEV == dev->dev_type) {
struct mvs_slot_info *slot = task->lldd_task;
u32 slot_idx = (u32)(slot - mvi->slot_info);
mv_dprintk("mvs_abort_task() mvi=%p task=%p "
diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h
index 9f3cc13a5ce7..60e2fb7f2dca 100644
--- a/drivers/scsi/mvsas/mv_sas.h
+++ b/drivers/scsi/mvsas/mv_sas.h
@@ -67,7 +67,7 @@ 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))
+ ((type == SAS_EDGE_EXPANDER_DEVICE) || (type == SAS_FANOUT_EXPANDER_DEVICE))
#define bit(n) ((u64)1 << n)
@@ -241,7 +241,7 @@ 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;
diff --git a/drivers/scsi/pm8001/Makefile b/drivers/scsi/pm8001/Makefile
index 52f04296171c..ce4cd87c7c66 100644
--- a/drivers/scsi/pm8001/Makefile
+++ b/drivers/scsi/pm8001/Makefile
@@ -4,9 +4,10 @@
# Copyright (C) 2008-2009 USI Co., Ltd.
-obj-$(CONFIG_SCSI_PM8001) += pm8001.o
-pm8001-y += pm8001_init.o \
+obj-$(CONFIG_SCSI_PM8001) += pm80xx.o
+pm80xx-y += pm8001_init.o \
pm8001_sas.o \
pm8001_ctl.o \
- pm8001_hwi.o
+ pm8001_hwi.o \
+ pm80xx_hwi.o
diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c
index 45bc197bc22f..d99f41c2ca13 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.
@@ -58,8 +58,13 @@ 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 snprintf(buf, PAGE_SIZE, "%d\n",
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.interface_rev);
+ } else {
+ return snprintf(buf, PAGE_SIZE, "%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);
@@ -78,11 +83,19 @@ 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 snprintf(buf, PAGE_SIZE, "%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 snprintf(buf, PAGE_SIZE, "%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);
/**
@@ -99,8 +112,13 @@ 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 snprintf(buf, PAGE_SIZE, "%d\n",
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.max_out_io);
+ } else {
+ return snprintf(buf, PAGE_SIZE, "%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);
/**
@@ -117,8 +135,15 @@ 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 snprintf(buf, PAGE_SIZE, "%04d\n",
+ (u16)(pm8001_ha->main_cfg_tbl.pm8001_tbl.max_sgl >> 16)
+ );
+ } else {
+ return snprintf(buf, PAGE_SIZE, "%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);
/**
@@ -136,8 +161,15 @@ 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 snprintf(buf, PAGE_SIZE, "%04d\n",
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.max_sgl & 0x0000FFFF
+ );
+ } else {
+ return snprintf(buf, PAGE_SIZE, "%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);
@@ -173,7 +205,14 @@ 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,
@@ -361,10 +400,11 @@ static int pm8001_set_nvmd(struct pm8001_hba_info *pm8001_ha)
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->id = 0;
+ payload->minor_function = 0x1;
pm8001_ha->nvmd_completion = &completion;
ret = PM8001_CHIP_DISP->set_nvmd_req(pm8001_ha, payload);
wait_for_completion(&completion);
@@ -411,7 +451,7 @@ static int pm8001_update_flash(struct pm8001_hba_info *pm8001_ha)
payload->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 */
diff --git a/drivers/scsi/pm8001/pm8001_defs.h b/drivers/scsi/pm8001/pm8001_defs.h
index c3d20c8d4abe..479c5a7a863a 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,9 +43,12 @@
enum chip_flavors {
chip_8001,
+ chip_8008,
+ chip_8009,
+ chip_8018,
+ chip_8019
};
-#define USI_MAX_MEMCNT 9
-#define PM8001_MAX_DMA_SG SG_ALL
+
enum phy_speed {
PHY_SPEED_15 = 0x01,
PHY_SPEED_30 = 0x02,
@@ -69,23 +72,34 @@ enum port_type {
#define PM8001_MPI_QUEUE 1024 /* maximum mpi queue entries */
#define PM8001_MAX_INB_NUM 1
#define PM8001_MAX_OUTB_NUM 1
+#define PM8001_MAX_SPCV_INB_NUM 1
+#define PM8001_MAX_SPCV_OUTB_NUM 4
#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 USI_MAX_MEMCNT_BASE 5
+#define IB (USI_MAX_MEMCNT_BASE + 1)
+#define CI (IB + PM8001_MAX_SPCV_INB_NUM)
+#define OB (CI + PM8001_MAX_SPCV_INB_NUM)
+#define PI (OB + PM8001_MAX_SPCV_OUTB_NUM)
+#define USI_MAX_MEMCNT (PI + PM8001_MAX_SPCV_OUTB_NUM)
+#define PM8001_MAX_DMA_SG SG_ALL
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 */
};
#define PM8001_EVENT_LOG_SIZE (128 * 1024)
diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c
index b8dd05074abb..69dd49c05f1e 100644
--- a/drivers/scsi/pm8001/pm8001_hwi.c
+++ b/drivers/scsi/pm8001/pm8001_hwi.c
@@ -50,32 +50,39 @@
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);
}
@@ -86,31 +93,56 @@ static void read_main_config_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);
}
/**
@@ -119,10 +151,9 @@ static void read_general_status_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,10 +168,9 @@ static void read_inbnd_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)));
@@ -155,54 +185,57 @@ static void read_outbnd_queue_table(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 =
+ 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_MAX_INB_NUM; i++) {
pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt =
PM8001_MPI_QUEUE | (64 << 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 + 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 + 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 + i].virt_ptr;
pm8001_ha->inbnd_q_tbl[i].total_length =
- pm8001_ha->memoryMap.region[IB].total_len;
+ pm8001_ha->memoryMap.region[IB + 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 + 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 + i].phys_addr_lo;
pm8001_ha->inbnd_q_tbl[i].ci_virt =
- pm8001_ha->memoryMap.region[CI].virt_ptr;
+ pm8001_ha->memoryMap.region[CI + i].virt_ptr;
offsetib = i * 0x20;
pm8001_ha->inbnd_q_tbl[i].pi_pci_bar =
get_pci_bar_index(pm8001_mr32(addressib,
@@ -212,25 +245,25 @@ static void 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_MAX_OUTB_NUM; i++) {
pm8001_ha->outbnd_q_tbl[i].element_size_cnt =
PM8001_MPI_QUEUE | (64 << 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 + 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 + 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 + i].virt_ptr;
pm8001_ha->outbnd_q_tbl[i].total_length =
- pm8001_ha->memoryMap.region[OB].total_len;
+ pm8001_ha->memoryMap.region[OB + 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 + 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 + 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 + i].virt_ptr;
offsetob = i * 0x24;
pm8001_ha->outbnd_q_tbl[i].ci_pci_bar =
get_pci_bar_index(pm8001_mr32(addressob,
@@ -250,42 +283,51 @@ 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);
}
/**
@@ -597,6 +639,19 @@ static void init_pci_device_addresses(struct pm8001_hba_info *pm8001_ha)
*/
static int pm8001_chip_init(struct pm8001_hba_info *pm8001_ha)
{
+ u8 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) {
+ if (-1 == pm8001_bar4_shift(pm8001_ha, GSM_SM_BASE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("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,
@@ -613,11 +668,16 @@ static int 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);
- /* 7->130ms, 34->500ms, 119->1.5s */
- mpi_set_open_retry_interval_reg(pm8001_ha, 119);
+ for (i = 0; i < PM8001_MAX_INB_NUM; i++)
+ update_inbnd_queue_table(pm8001_ha, i);
+ for (i = 0; i < PM8001_MAX_OUTB_NUM; i++)
+ update_outbnd_queue_table(pm8001_ha, i);
+ /* 8081 controller donot require these operations */
+ if (deviceid != 0x8081) {
+ 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,
@@ -639,6 +699,16 @@ 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) {
+ if (-1 == pm8001_bar4_shift(pm8001_ha, GSM_SM_BASE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("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 */
@@ -740,14 +810,14 @@ 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 */
@@ -1113,7 +1183,7 @@ static void pm8001_hw_chip_rst(struct pm8001_hba_info *pm8001_ha)
* pm8001_chip_iounmap - which maped 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++) {
@@ -1192,7 +1262,7 @@ pm8001_chip_msix_interrupt_disable(struct pm8001_hba_info *pm8001_ha,
* @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);
@@ -1207,7 +1277,7 @@ pm8001_chip_interrupt_enable(struct pm8001_hba_info *pm8001_ha)
* @pm8001_ha: our hba card information
*/
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);
@@ -1218,12 +1288,13 @@ pm8001_chip_interrupt_disable(struct pm8001_hba_info *pm8001_ha)
}
/**
- * 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;
@@ -1231,7 +1302,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;
}
@@ -1245,7 +1316,7 @@ static int mpi_msg_free_get(struct inbound_queue_table *circularQ,
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)
% PM8001_MPI_QUEUE;
@@ -1257,29 +1328,30 @@ 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.
* @opCode: the operation code represents commands which LLDD and fw recognized.
* @payload: the command payload of each operation command.
*/
-static int mpi_build_cmd(struct pm8001_hba_info *pm8001_ha,
+int pm8001_mpi_build_cmd(struct pm8001_hba_info *pm8001_ha,
struct inbound_queue_table *circularQ,
- u32 opCode, void *payload)
+ u32 opCode, void *payload, 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) {
+ if (pm8001_mpi_msg_free_get(circularQ, pm8001_ha->iomb_size,
+ &pMessage) < 0) {
PM8001_IO_DBG(pm8001_ha,
pm8001_printk("No free mpi buffer\n"));
return -1;
}
BUG_ON(!payload);
/*Copy to the payload*/
- memcpy(pMessage, payload, (64 - sizeof(struct mpi_msg_hdr)));
+ memcpy(pMessage, payload, (pm8001_ha->iomb_size -
+ sizeof(struct mpi_msg_hdr)));
/*Build the header*/
Header = ((1 << 31) | (hpriority << 30) | ((bc & 0x1f) << 24)
@@ -1291,12 +1363,13 @@ static int mpi_build_cmd(struct pm8001_hba_info *pm8001_ha,
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));
+ pm8001_printk("INB Q %x OPCODE:%x , UPDATED PI=%d CI=%d\n",
+ responseQueue, opCode, circularQ->producer_idx,
+ circularQ->consumer_index));
return 0;
}
-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;
@@ -1305,7 +1378,7 @@ 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",
@@ -1336,13 +1409,14 @@ static u32 mpi_msg_free_set(struct pm8001_hba_info *pm8001_ha, void *pMsg,
}
/**
- * 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)
{
@@ -1356,7 +1430,7 @@ static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
/*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);
@@ -1416,7 +1490,7 @@ static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
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;
@@ -1431,7 +1505,7 @@ static void pm8001_work_fn(struct work_struct *work)
pm8001_dev = pw->data; /* Most stash device structure */
if ((pm8001_dev == NULL)
|| ((pw->handler != IO_XFER_ERROR_BREAK)
- && (pm8001_dev->dev_type == NO_DEVICE))) {
+ && (pm8001_dev->dev_type == SAS_PHY_UNUSED))) {
kfree(pw);
return;
}
@@ -1596,7 +1670,7 @@ static void pm8001_work_fn(struct work_struct *work)
} break;
case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
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:
dev = pm8001_dev->sas_device;
@@ -1614,7 +1688,7 @@ static void pm8001_work_fn(struct work_struct *work)
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;
@@ -1633,6 +1707,123 @@ static int pm8001_handle_event(struct pm8001_hba_info *pm8001_ha, void *data,
return ret;
}
+static void pm8001_send_abort_all(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_ha_dev)
+{
+ int res;
+ u32 ccb_tag;
+ struct pm8001_ccb_info *ccb;
+ struct sas_task *task = NULL;
+ struct task_abort_req task_abort;
+ struct inbound_queue_table *circularQ;
+ u32 opc = OPC_INB_SATA_ABORT;
+ int ret;
+
+ if (!pm8001_ha_dev) {
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("dev is null\n"));
+ return;
+ }
+
+ task = sas_alloc_slow_task(GFP_ATOMIC);
+
+ if (!task) {
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("cannot "
+ "allocate task\n"));
+ return;
+ }
+
+ task->task_done = pm8001_task_done;
+
+ res = pm8001_tag_alloc(pm8001_ha, &ccb_tag);
+ if (res)
+ return;
+
+ ccb = &pm8001_ha->ccb_info[ccb_tag];
+ ccb->device = pm8001_ha_dev;
+ ccb->ccb_tag = ccb_tag;
+ ccb->task = task;
+
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+
+ memset(&task_abort, 0, sizeof(task_abort));
+ task_abort.abort_all = cpu_to_le32(1);
+ task_abort.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
+ task_abort.tag = cpu_to_le32(ccb_tag);
+
+ ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &task_abort, 0);
+
+}
+
+static void pm8001_send_read_log(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_ha_dev)
+{
+ struct sata_start_req sata_cmd;
+ int res;
+ u32 ccb_tag;
+ struct pm8001_ccb_info *ccb;
+ struct sas_task *task = NULL;
+ struct host_to_dev_fis fis;
+ struct domain_device *dev;
+ struct inbound_queue_table *circularQ;
+ u32 opc = OPC_INB_SATA_HOST_OPSTART;
+
+ task = sas_alloc_slow_task(GFP_ATOMIC);
+
+ if (!task) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("cannot allocate task !!!\n"));
+ return;
+ }
+ task->task_done = pm8001_task_done;
+
+ res = pm8001_tag_alloc(pm8001_ha, &ccb_tag);
+ if (res) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("cannot allocate tag !!!\n"));
+ return;
+ }
+
+ /* allocate domain device by ourselves as libsas
+ * is not going to provide any
+ */
+ dev = kzalloc(sizeof(struct domain_device), GFP_ATOMIC);
+ if (!dev) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Domain device cannot be allocated\n"));
+ sas_free_task(task);
+ return;
+ } else {
+ task->dev = dev;
+ task->dev->lldd_dev = pm8001_ha_dev;
+ }
+
+ ccb = &pm8001_ha->ccb_info[ccb_tag];
+ ccb->device = pm8001_ha_dev;
+ ccb->ccb_tag = ccb_tag;
+ ccb->task = task;
+ pm8001_ha_dev->id |= NCQ_READ_LOG_FLAG;
+ pm8001_ha_dev->id |= NCQ_2ND_RLE_FLAG;
+
+ memset(&sata_cmd, 0, sizeof(sata_cmd));
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+
+ /* construct read log FIS */
+ memset(&fis, 0, sizeof(struct host_to_dev_fis));
+ fis.fis_type = 0x27;
+ fis.flags = 0x80;
+ fis.command = ATA_CMD_READ_LOG_EXT;
+ fis.lbal = 0x10;
+ fis.sector_count = 0x1;
+
+ sata_cmd.tag = cpu_to_le32(ccb_tag);
+ sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
+ sata_cmd.ncqtag_atap_dir_m |= ((0x1 << 7) | (0x5 << 9));
+ memcpy(&sata_cmd.sata_fis, &fis, sizeof(struct host_to_dev_fis));
+
+ res = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &sata_cmd, 0);
+
+}
+
/**
* mpi_ssp_completion- process the event that FW response to the SSP request.
* @pm8001_ha: our hba card information
@@ -1867,7 +2058,7 @@ mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb)
break;
}
PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("scsi_status = %x \n ",
+ pm8001_printk("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;
@@ -2096,16 +2287,44 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
status = le32_to_cpu(psataPayload->status);
tag = le32_to_cpu(psataPayload->tag);
+ if (!tag) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("tag null\n"));
+ return;
+ }
ccb = &pm8001_ha->ccb_info[tag];
param = le32_to_cpu(psataPayload->param);
- t = ccb->task;
+ if (ccb) {
+ t = ccb->task;
+ pm8001_dev = ccb->device;
+ } else {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("ccb null\n"));
+ return;
+ }
+
+ if (t) {
+ if (t->dev && (t->dev->lldd_dev))
+ pm8001_dev = t->dev->lldd_dev;
+ } else {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("task null\n"));
+ return;
+ }
+
+ if ((pm8001_dev && !(pm8001_dev->id & NCQ_READ_LOG_FLAG))
+ && unlikely(!t || !t->lldd_task || !t->dev)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("task or dev null\n"));
+ return;
+ }
+
ts = &t->task_status;
- pm8001_dev = ccb->device;
- if (status)
+ if (!ts) {
PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("sata IO status 0x%x\n", status));
- if (unlikely(!t || !t->lldd_task || !t->dev))
+ pm8001_printk("ts null\n"));
return;
+ }
switch (status) {
case IO_SUCCESS:
@@ -2113,6 +2332,19 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
if (param == 0) {
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAM_STAT_GOOD;
+ /* check if response is for SEND READ LOG */
+ if (pm8001_dev &&
+ (pm8001_dev->id & NCQ_READ_LOG_FLAG)) {
+ /* set new bit for abort_all */
+ pm8001_dev->id |= NCQ_ABORT_ALL_FLAG;
+ /* clear bit for read log */
+ pm8001_dev->id = pm8001_dev->id & 0x7FFFFFFF;
+ pm8001_send_abort_all(pm8001_ha, pm8001_dev);
+ /* Free the tag */
+ pm8001_tag_free(pm8001_ha, tag);
+ sas_free_task(t);
+ return;
+ }
} else {
u8 len;
ts->resp = SAS_TASK_COMPLETE;
@@ -2424,6 +2656,29 @@ static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
unsigned long flags;
ccb = &pm8001_ha->ccb_info[tag];
+
+ if (ccb) {
+ t = ccb->task;
+ pm8001_dev = ccb->device;
+ } else {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("No CCB !!!. returning\n"));
+ }
+ if (event)
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("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);
+ /* send read log extension */
+ if (pm8001_dev)
+ pm8001_send_read_log(pm8001_ha, pm8001_dev);
+ return;
+ }
+
+ ccb = &pm8001_ha->ccb_info[tag];
t = ccb->task;
pm8001_dev = ccb->device;
if (event)
@@ -2432,9 +2687,9 @@ static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
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_IO_DBG(pm8001_ha, pm8001_printk(
+ "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"));
@@ -2822,8 +3077,8 @@ mpi_smp_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
}
}
-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);
@@ -2843,8 +3098,7 @@ mpi_set_dev_state_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
pm8001_ccb_free(pm8001_ha, tag);
}
-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);
@@ -2863,8 +3117,8 @@ mpi_set_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
pm8001_ccb_free(pm8001_ha, tag);
}
-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 get_nvm_data_resp *pPayload =
@@ -2925,7 +3179,7 @@ mpi_get_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
pm8001_ccb_free(pm8001_ha, tag);
}
-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)
{
struct local_phy_ctl_resp *pPayload =
(struct local_phy_ctl_resp *)(piomb + 4);
@@ -2954,7 +3208,7 @@ static int mpi_local_phy_ctl(struct pm8001_hba_info *pm8001_ha, void *piomb)
* 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;
@@ -2988,7 +3242,7 @@ static void pm8001_bytes_dmaed(struct pm8001_hba_info *pm8001_ha, int i)
}
/* 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;
@@ -3025,7 +3279,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
@@ -3067,7 +3321,7 @@ static void pm8001_hw_event_ack_req(struct pm8001_hba_info *pm8001_ha,
((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, circularQ, opc, &payload, 0);
}
static int pm8001_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha,
@@ -3112,19 +3366,19 @@ hw_event_sas_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)
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"));
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"));
port->port_attached = 1;
- get_lrate_mode(phy, link_rate);
+ pm8001_get_lrate_mode(phy, link_rate);
break;
default:
PM8001_MSG_DBG(pm8001_ha,
@@ -3179,7 +3433,7 @@ hw_event_sata_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)
" phy id = %d\n", port_id, phy_id));
port->port_state = portstate;
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;
@@ -3189,7 +3443,7 @@ hw_event_sata_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)
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);
@@ -3260,7 +3514,7 @@ hw_event_phy_down(struct pm8001_hba_info *pm8001_ha, void *piomb)
}
/**
- * 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
*
@@ -3269,7 +3523,7 @@ hw_event_phy_down(struct pm8001_hba_info *pm8001_ha, void *piomb)
* 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;
@@ -3331,7 +3585,7 @@ static int mpi_reg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
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;
@@ -3347,8 +3601,13 @@ static int mpi_dereg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
return 0;
}
-static int
-mpi_fw_flash_update_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+/**
+ * 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;
@@ -3403,10 +3662,6 @@ mpi_fw_flash_update_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
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);
complete(pm8001_ha->nvmd_completion);
ccb->task = NULL;
ccb->ccb_tag = 0xFFFFFFFF;
@@ -3414,8 +3669,7 @@ mpi_fw_flash_update_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
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;
@@ -3431,8 +3685,7 @@ mpi_general_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
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;
@@ -3440,19 +3693,29 @@ 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);
status = le32_to_cpu(pPayload->status);
tag = le32_to_cpu(pPayload->tag);
+ if (!tag) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk(" TAG NULL. RETURNING !!!"));
+ return -1;
+ }
+
scp = le32_to_cpu(pPayload->scp);
ccb = &pm8001_ha->ccb_info[tag];
t = ccb->task;
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk(" status = 0x%x\n", status));
- if (t == NULL)
+ pm8001_dev = ccb->device; /* retrieve device */
+
+ if (!t) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk(" TASK NULL. RETURNING !!!"));
return -1;
+ }
ts = &t->task_status;
if (status != 0)
PM8001_FAIL_DBG(pm8001_ha,
@@ -3476,7 +3739,15 @@ mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
spin_unlock_irqrestore(&t->task_state_lock, flags);
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();
- t->task_done(t);
+
+ if ((pm8001_dev->id & NCQ_ABORT_ALL_FLAG) && t) {
+ pm8001_tag_free(pm8001_ha, tag);
+ sas_free_task(t);
+ /* clear the flag */
+ pm8001_dev->id &= 0xBFFFFFFF;
+ } else
+ t->task_done(t);
+
return 0;
}
@@ -3727,17 +3998,17 @@ static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
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_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_mpi_reg_resp(pm8001_ha, piomb);
break;
case OPC_OUB_DEREG_DEV:
PM8001_MSG_DBG(pm8001_ha,
pm8001_printk("unregister the device\n"));
- mpi_dereg_resp(pm8001_ha, piomb);
+ pm8001_mpi_dereg_resp(pm8001_ha, piomb);
break;
case OPC_OUB_GET_DEV_HANDLE:
PM8001_MSG_DBG(pm8001_ha,
@@ -3775,7 +4046,7 @@ static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
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_mpi_fw_flash_update_resp(pm8001_ha, piomb);
break;
case OPC_OUB_GPIO_RESPONSE:
PM8001_MSG_DBG(pm8001_ha,
@@ -3788,17 +4059,17 @@ static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
case OPC_OUB_GENERAL_EVENT:
PM8001_MSG_DBG(pm8001_ha,
pm8001_printk("OPC_OUB_GENERAL_EVENT\n"));
- mpi_general_event(pm8001_ha, piomb);
+ 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_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_mpi_task_abort_resp(pm8001_ha, piomb);
break;
case OPC_OUB_SAS_DIAG_MODE_START_END:
PM8001_MSG_DBG(pm8001_ha,
@@ -3823,17 +4094,17 @@ static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
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_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_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_mpi_set_nvmd_resp(pm8001_ha, piomb);
break;
case OPC_OUB_DEVICE_HANDLE_REMOVAL:
PM8001_MSG_DBG(pm8001_ha,
@@ -3842,7 +4113,7 @@ static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
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_mpi_set_dev_state_resp(pm8001_ha, piomb);
break;
case OPC_OUB_GET_DEVICE_STATE:
PM8001_MSG_DBG(pm8001_ha,
@@ -3864,7 +4135,7 @@ static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
}
}
-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;
@@ -3873,14 +4144,15 @@ static int process_oq(struct pm8001_hba_info *pm8001_ha)
unsigned long flags;
spin_lock_irqsave(&pm8001_ha->lock, flags);
- circularQ = &pm8001_ha->outbnd_q_tbl[0];
+ 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) {
/* Update the producer index from SPC */
@@ -3903,7 +4175,7 @@ static const u8 data_dir_flags[] = {
[PCI_DMA_FROMDEVICE] = DATA_DIR_IN,/* INBOUND */
[PCI_DMA_NONE] = DATA_DIR_NONE,/* NO TRANSFER */
};
-static void
+void
pm8001_chip_make_sg(struct scatterlist *scatter, int nr, void *prd)
{
int i;
@@ -3978,7 +4250,7 @@ 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);
+ pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&smp_cmd, 0);
return 0;
err_out_2:
@@ -4042,7 +4314,7 @@ 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);
+ ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &ssp_cmd, 0);
return ret;
}
@@ -4060,6 +4332,7 @@ static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
u32 ATAP = 0x0;
u32 dir;
struct inbound_queue_table *circularQ;
+ unsigned long flags;
u32 opc = OPC_INB_SATA_HOST_OPSTART;
memset(&sata_cmd, 0, sizeof(sata_cmd));
circularQ = &pm8001_ha->inbnd_q_tbl[0];
@@ -4080,8 +4353,10 @@ static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
PM8001_IO_DBG(pm8001_ha, pm8001_printk("FPDMA\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);
@@ -4112,7 +4387,55 @@ 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);
+
+ /* Check for read log for failed drive and return */
+ if (sata_cmd.sata_fis.command == 0x2f) {
+ if (pm8001_ha_dev && ((pm8001_ha_dev->id & NCQ_READ_LOG_FLAG) ||
+ (pm8001_ha_dev->id & NCQ_ABORT_ALL_FLAG) ||
+ (pm8001_ha_dev->id & NCQ_2ND_RLE_FLAG))) {
+ struct task_status_struct *ts;
+
+ pm8001_ha_dev->id &= 0xDFFFFFFF;
+ ts = &task->task_status;
+
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_STAT_GOOD;
+ 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))) {
+ spin_unlock_irqrestore(&task->task_state_lock,
+ flags);
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("task 0x%p resp 0x%x "
+ " stat 0x%x but aborted by upper layer "
+ "\n", task, ts->resp, ts->stat));
+ pm8001_ccb_task_free(pm8001_ha, task, ccb, tag);
+ } else if (task->uldd_task) {
+ spin_unlock_irqrestore(&task->task_state_lock,
+ flags);
+ pm8001_ccb_task_free(pm8001_ha, task, ccb, tag);
+ mb();/* ditto */
+ spin_unlock_irq(&pm8001_ha->lock);
+ task->task_done(task);
+ spin_lock_irq(&pm8001_ha->lock);
+ return 0;
+ } else if (!task->uldd_task) {
+ spin_unlock_irqrestore(&task->task_state_lock,
+ flags);
+ pm8001_ccb_task_free(pm8001_ha, task, ccb, tag);
+ mb();/*ditto*/
+ spin_unlock_irq(&pm8001_ha->lock);
+ task->task_done(task);
+ spin_lock_irq(&pm8001_ha->lock);
+ return 0;
+ }
+ }
+ }
+
+ ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &sata_cmd, 0);
return ret;
}
@@ -4142,12 +4465,12 @@ 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);
payload.sas_identify.phy_id = phy_id;
- ret = mpi_build_cmd(pm8001_ha, circularQ, opcode, &payload);
+ ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opcode, &payload, 0);
return ret;
}
@@ -4157,7 +4480,7 @@ pm8001_chip_phy_start_req(struct pm8001_hba_info *pm8001_ha, u8 phy_id)
* @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,
+int pm8001_chip_phy_stop_req(struct pm8001_hba_info *pm8001_ha,
u8 phy_id)
{
struct phy_stop_req payload;
@@ -4169,12 +4492,12 @@ static int pm8001_chip_phy_stop_req(struct pm8001_hba_info *pm8001_ha,
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);
+ ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opcode, &payload, 0);
return ret;
}
/**
- * 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)
@@ -4204,11 +4527,11 @@ static int pm8001_chip_reg_dev_req(struct pm8001_hba_info *pm8001_ha,
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 ||
+ pm8001_dev->dev_type == SAS_EDGE_EXPANDER_DEVICE ||
+ pm8001_dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE)
stp_sspsmp_sata = 0x01; /*ssp or smp*/
}
if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type))
@@ -4228,14 +4551,14 @@ 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, circularQ, opc, &payload, 0);
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;
@@ -4249,7 +4572,7 @@ static int pm8001_chip_dereg_dev_req(struct pm8001_hba_info *pm8001_ha,
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);
+ ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
return ret;
}
@@ -4272,7 +4595,7 @@ static int pm8001_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha,
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);
+ ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
return ret;
}
@@ -4296,11 +4619,11 @@ static u32 pm8001_chip_is_our_interupt(struct pm8001_hba_info *pm8001_ha)
* @stat: stat.
*/
static irqreturn_t
-pm8001_chip_isr(struct pm8001_hba_info *pm8001_ha)
+pm8001_chip_isr(struct pm8001_hba_info *pm8001_ha, u8 vec)
{
- pm8001_chip_interrupt_disable(pm8001_ha);
- process_oq(pm8001_ha);
- pm8001_chip_interrupt_enable(pm8001_ha);
+ pm8001_chip_interrupt_disable(pm8001_ha, vec);
+ process_oq(pm8001_ha, vec);
+ pm8001_chip_interrupt_enable(pm8001_ha, vec);
return IRQ_HANDLED;
}
@@ -4322,7 +4645,7 @@ static int send_task_abort(struct pm8001_hba_info *pm8001_ha, u32 opc,
task_abort.device_id = cpu_to_le32(dev_id);
task_abort.tag = cpu_to_le32(cmd_tag);
}
- ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &task_abort);
+ ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &task_abort, 0);
return ret;
}
@@ -4331,16 +4654,17 @@ static int send_task_abort(struct pm8001_hba_info *pm8001_ha, u32 opc,
* @task: the task we wanted to aborted.
* @flag: the abort flag.
*/
-static int pm8001_chip_abort_task(struct pm8001_hba_info *pm8001_ha,
+int pm8001_chip_abort_task(struct pm8001_hba_info *pm8001_ha,
struct pm8001_device *pm8001_dev, u8 flag, u32 task_tag, u32 cmd_tag)
{
u32 opc, device_id;
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)
+ PM8001_EH_DBG(pm8001_ha,
+ pm8001_printk("cmd_tag = %x, abort task tag = 0x%x",
+ cmd_tag, task_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 */
@@ -4358,7 +4682,7 @@ 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,
+int pm8001_chip_ssp_tm_req(struct pm8001_hba_info *pm8001_ha,
struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf)
{
struct sas_task *task = ccb->task;
@@ -4376,11 +4700,11 @@ static int pm8001_chip_ssp_tm_req(struct pm8001_hba_info *pm8001_ha,
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);
+ ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &sspTMCmd, 0);
return ret;
}
-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;
@@ -4397,7 +4721,7 @@ 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->usrAddr = (u8 *)ioctl_payload->func_specific;
fw_control_context->len = ioctl_payload->length;
circularQ = &pm8001_ha->inbnd_q_tbl[0];
memset(&nvmd_req, 0, sizeof(nvmd_req));
@@ -4456,11 +4780,11 @@ static int pm8001_chip_get_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, circularQ, opc, &nvmd_req, 0);
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;
@@ -4479,7 +4803,7 @@ static int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha,
return -ENOMEM;
circularQ = &pm8001_ha->inbnd_q_tbl[0];
memcpy(pm8001_ha->memoryMap.region[NVMD].virt_ptr,
- ioctl_payload->func_specific,
+ &ioctl_payload->func_specific,
ioctl_payload->length);
memset(&nvmd_req, 0, sizeof(nvmd_req));
rc = pm8001_tag_alloc(pm8001_ha, &tag);
@@ -4536,7 +4860,7 @@ 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, circularQ, opc, &nvmd_req, 0);
return rc;
}
@@ -4545,7 +4869,7 @@ static int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha,
* @pm8001_ha: our hba card information.
* @fw_flash_updata_info: firmware flash update param
*/
-static int
+int
pm8001_chip_fw_flash_update_build(struct pm8001_hba_info *pm8001_ha,
void *fw_flash_updata_info, u32 tag)
{
@@ -4567,11 +4891,11 @@ pm8001_chip_fw_flash_update_build(struct pm8001_hba_info *pm8001_ha,
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)));
- ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+ ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
return ret;
}
-static int
+int
pm8001_chip_fw_flash_update_req(struct pm8001_hba_info *pm8001_ha,
void *payload)
{
@@ -4581,29 +4905,14 @@ pm8001_chip_fw_flash_update_req(struct pm8001_hba_info *pm8001_ha,
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;
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);
@@ -4613,6 +4922,7 @@ 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) {
@@ -4627,7 +4937,7 @@ pm8001_chip_fw_flash_update_req(struct pm8001_hba_info *pm8001_ha,
return rc;
}
-static int
+int
pm8001_chip_set_dev_state_req(struct pm8001_hba_info *pm8001_ha,
struct pm8001_device *pm8001_dev, u32 state)
{
@@ -4648,7 +4958,7 @@ pm8001_chip_set_dev_state_req(struct pm8001_hba_info *pm8001_ha,
payload.tag = cpu_to_le32(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);
+ rc = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
return rc;
}
@@ -4673,7 +4983,7 @@ pm8001_chip_sas_re_initialization(struct pm8001_hba_info *pm8001_ha)
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);
+ rc = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
return rc;
}
@@ -4706,4 +5016,3 @@ const struct pm8001_dispatch pm8001_8001_dispatch = {
.set_dev_state_req = pm8001_chip_set_dev_state_req,
.sas_re_init_req = pm8001_chip_sas_re_initialization,
};
-
diff --git a/drivers/scsi/pm8001/pm8001_hwi.h b/drivers/scsi/pm8001/pm8001_hwi.h
index d437309cb1e1..d7c1e2034226 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
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c
index 3d5e522e00fc..e4b9bc7f5410 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.
@@ -44,8 +44,16 @@
static struct scsi_transport_template *pm8001_stt;
+/**
+ * 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,},
};
static int pm8001_id;
@@ -155,37 +163,75 @@ static void pm8001_free(struct pm8001_hba_info *pm8001_ha)
}
#ifdef PM8001_USE_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;
+ u32 vec;
pm8001_ha = (struct pm8001_hba_info *)opaque;
if (unlikely(!pm8001_ha))
BUG_ON(1);
- PM8001_CHIP_DISP->isr(pm8001_ha);
+ vec = pm8001_ha->int_vector;
+ PM8001_CHIP_DISP->isr(pm8001_ha, vec);
+}
+#endif
+
+static struct pm8001_hba_info *outq_to_hba(u8 *outq)
+{
+ return container_of((outq - *outq), struct pm8001_hba_info, outq[0]);
}
+
+/**
+ * pm8001_interrupt_handler_msix - main MSIX interrupt handler.
+ * It obtains the vector number and calls the equivalent bottom
+ * half or services directly.
+ * @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 pm8001_hba_info *pm8001_ha = outq_to_hba(opaque);
+ u8 outq = *(u8 *)opaque;
+ irqreturn_t ret = IRQ_HANDLED;
+ if (unlikely(!pm8001_ha))
+ return IRQ_NONE;
+ if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha))
+ return IRQ_NONE;
+ pm8001_ha->int_vector = outq;
+#ifdef PM8001_USE_TASKLET
+ tasklet_schedule(&pm8001_ha->tasklet);
+#else
+ ret = PM8001_CHIP_DISP->isr(pm8001_ha, outq);
#endif
+ return ret;
+}
+/**
+ * pm8001_interrupt_handler_intx - main INTx interrupt handler.
+ * @dev_id: sas_ha structure. The HBA is retrieved from sas_has structure.
+ */
- /**
- * 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 irqreturn_t pm8001_interrupt_handler_intx(int irq, void *dev_id)
{
struct pm8001_hba_info *pm8001_ha;
irqreturn_t ret = IRQ_HANDLED;
- struct sas_ha_struct *sha = opaque;
+ struct sas_ha_struct *sha = dev_id;
pm8001_ha = sha->lldd_ha;
if (unlikely(!pm8001_ha))
return IRQ_NONE;
if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha))
return IRQ_NONE;
+
+ pm8001_ha->int_vector = 0;
#ifdef PM8001_USE_TASKLET
tasklet_schedule(&pm8001_ha->tasklet);
#else
- ret = PM8001_CHIP_DISP->isr(pm8001_ha);
+ ret = PM8001_CHIP_DISP->isr(pm8001_ha, 0);
#endif
return ret;
}
@@ -195,10 +241,14 @@ static irqreturn_t pm8001_interrupt(int irq, void *opaque)
* @pm8001_ha:our hba structure.
*
*/
-static int 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;
spin_lock_init(&pm8001_ha->lock);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("pm8001_alloc: PHY:%x\n",
+ pm8001_ha->chip->n_phy));
for (i = 0; i < pm8001_ha->chip->n_phy; i++) {
pm8001_phy_init(pm8001_ha, i);
pm8001_ha->port[i].wide_port_phymap = 0;
@@ -222,30 +272,57 @@ static int 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 = PM8001_MPI_QUEUE;
- pm8001_ha->memoryMap.region[IB].element_size = 64;
- pm8001_ha->memoryMap.region[IB].total_len = PM8001_MPI_QUEUE * 64;
- pm8001_ha->memoryMap.region[IB].alignment = 64;
-
- /* MPI Memory region 6 outbound queues */
- pm8001_ha->memoryMap.region[OB].num_elements = PM8001_MPI_QUEUE;
- pm8001_ha->memoryMap.region[OB].element_size = 64;
- pm8001_ha->memoryMap.region[OB].total_len = PM8001_MPI_QUEUE * 64;
- pm8001_ha->memoryMap.region[OB].alignment = 64;
+ for (i = 0; i < PM8001_MAX_SPCV_INB_NUM; i++) {
+ /* MPI Memory region 3 for consumer Index of inbound queues */
+ pm8001_ha->memoryMap.region[CI+i].num_elements = 1;
+ pm8001_ha->memoryMap.region[CI+i].element_size = 4;
+ pm8001_ha->memoryMap.region[CI+i].total_len = 4;
+ pm8001_ha->memoryMap.region[CI+i].alignment = 4;
+
+ if ((ent->driver_data) != chip_8001) {
+ /* MPI Memory region 5 inbound queues */
+ pm8001_ha->memoryMap.region[IB+i].num_elements =
+ PM8001_MPI_QUEUE;
+ pm8001_ha->memoryMap.region[IB+i].element_size = 128;
+ pm8001_ha->memoryMap.region[IB+i].total_len =
+ PM8001_MPI_QUEUE * 128;
+ pm8001_ha->memoryMap.region[IB+i].alignment = 128;
+ } else {
+ pm8001_ha->memoryMap.region[IB+i].num_elements =
+ PM8001_MPI_QUEUE;
+ pm8001_ha->memoryMap.region[IB+i].element_size = 64;
+ pm8001_ha->memoryMap.region[IB+i].total_len =
+ PM8001_MPI_QUEUE * 64;
+ pm8001_ha->memoryMap.region[IB+i].alignment = 64;
+ }
+ }
+
+ for (i = 0; i < PM8001_MAX_SPCV_OUTB_NUM; i++) {
+ /* MPI Memory region 4 for producer Index of outbound queues */
+ pm8001_ha->memoryMap.region[PI+i].num_elements = 1;
+ pm8001_ha->memoryMap.region[PI+i].element_size = 4;
+ pm8001_ha->memoryMap.region[PI+i].total_len = 4;
+ pm8001_ha->memoryMap.region[PI+i].alignment = 4;
+
+ if (ent->driver_data != chip_8001) {
+ /* MPI Memory region 6 Outbound queues */
+ pm8001_ha->memoryMap.region[OB+i].num_elements =
+ PM8001_MPI_QUEUE;
+ pm8001_ha->memoryMap.region[OB+i].element_size = 128;
+ pm8001_ha->memoryMap.region[OB+i].total_len =
+ PM8001_MPI_QUEUE * 128;
+ pm8001_ha->memoryMap.region[OB+i].alignment = 128;
+ } else {
+ /* MPI Memory region 6 Outbound queues */
+ pm8001_ha->memoryMap.region[OB+i].num_elements =
+ PM8001_MPI_QUEUE;
+ pm8001_ha->memoryMap.region[OB+i].element_size = 64;
+ pm8001_ha->memoryMap.region[OB+i].total_len =
+ PM8001_MPI_QUEUE * 64;
+ pm8001_ha->memoryMap.region[OB+i].alignment = 64;
+ }
+ }
/* Memory region write DMA*/
pm8001_ha->memoryMap.region[NVMD].num_elements = 1;
pm8001_ha->memoryMap.region[NVMD].element_size = 4096;
@@ -264,6 +341,9 @@ static int pm8001_alloc(struct pm8001_hba_info *pm8001_ha)
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;
+
for (i = 0; i < USI_MAX_MEMCNT; i++) {
if (pm8001_mem_alloc(pm8001_ha->pdev,
&pm8001_ha->memoryMap.region[i].virt_ptr,
@@ -281,7 +361,7 @@ static int pm8001_alloc(struct pm8001_hba_info *pm8001_ha)
pm8001_ha->devices = pm8001_ha->memoryMap.region[DEV_MEM].virt_ptr;
for (i = 0; i < PM8001_MAX_DEVICES; i++) {
- pm8001_ha->devices[i].dev_type = NO_DEVICE;
+ pm8001_ha->devices[i].dev_type = SAS_PHY_UNUSED;
pm8001_ha->devices[i].id = i;
pm8001_ha->devices[i].device_id = PM8001_MAX_DEVICES;
pm8001_ha->devices[i].running_req = 0;
@@ -339,10 +419,12 @@ static int pm8001_ioremap(struct pm8001_hba_info *pm8001_ha)
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_printk("PCI: bar %d, logicalBar %d ",
+ bar, logicalBar));
+ PM8001_INIT_DBG(pm8001_ha, pm8001_printk(
+ "base addr %llx virt_addr=%llx len=%d\n",
+ (u64)pm8001_ha->io_mem[logicalBar].membase,
+ (u64)pm8001_ha->io_mem[logicalBar].memvirtaddr,
pm8001_ha->io_mem[logicalBar].memsize));
} else {
pm8001_ha->io_mem[logicalBar].membase = 0;
@@ -361,8 +443,9 @@ static int pm8001_ioremap(struct pm8001_hba_info *pm8001_ha)
* @shost: scsi host struct which has been initialized before.
*/
static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev,
- u32 chip_id,
- struct Scsi_Host *shost)
+ 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);
@@ -374,7 +457,7 @@ static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev,
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;
@@ -382,12 +465,22 @@ static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev,
pm8001_ha->id = pm8001_id++;
pm8001_ha->logging_level = 0x01;
sprintf(pm8001_ha->name, "%s%d", DRV_NAME, pm8001_ha->id);
+ /* 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;
+
#ifdef PM8001_USE_TASKLET
+ /**
+ * default tasklet for non msi-x interrupt handler/first msi-x
+ * interrupt handler
+ **/
tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet,
- (unsigned long)pm8001_ha);
+ (unsigned long)pm8001_ha);
#endif
pm8001_ioremap(pm8001_ha);
- if (!pm8001_alloc(pm8001_ha))
+ if (!pm8001_alloc(pm8001_ha, ent))
return pm8001_ha;
pm8001_free(pm8001_ha);
return NULL;
@@ -512,21 +605,50 @@ static void pm8001_post_sas_ha_init(struct Scsi_Host *shost,
*/
static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha)
{
- u8 i;
+ u8 i, j;
#ifdef PM8001_READ_VPD
+ /* For new SPC controllers WWN is stored in flash vpd
+ * For SPC/SPCve controllers WWN is stored in EEPROM
+ * For Older SPC WWN is stored in NVMD
+ */
DECLARE_COMPLETION_ONSTACK(completion);
struct pm8001_ioctl_payload payload;
+ u16 deviceid;
+ pci_read_config_word(pm8001_ha->pdev, PCI_DEVICE_ID, &deviceid);
pm8001_ha->nvmd_completion = &completion;
- payload.minor_function = 0;
- payload.length = 128;
- payload.func_specific = kzalloc(128, GFP_KERNEL);
+
+ if (pm8001_ha->chip_id == chip_8001) {
+ if (deviceid == 0x8081) {
+ payload.minor_function = 4;
+ payload.length = 4096;
+ } else {
+ payload.minor_function = 0;
+ payload.length = 128;
+ }
+ } else {
+ payload.minor_function = 1;
+ payload.length = 4096;
+ }
+ payload.offset = 0;
+ payload.func_specific = kzalloc(payload.length, GFP_KERNEL);
PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, &payload);
wait_for_completion(&completion);
+
+ 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
+ pm8001_ha->sas_addr[j] =
+ payload.func_specific[0x804 + i];
+ }
+
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);
+ 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_printk("phy %d sas_addr = %016llx\n", i,
pm8001_ha->phy[i].dev_sas_addr));
}
#else
@@ -547,31 +669,50 @@ static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha)
* @chip_info: our ha struct.
* @irq_handler: irq_handler
*/
-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;
+ u32 number_of_intr;
int flag = 0;
u32 max_entry;
int rc;
+ static char intr_drvname[PM8001_MAX_MSIX_VEC][sizeof(DRV_NAME)+3];
+
+ /* SPCv controllers supports 64 msi-x */
+ if (pm8001_ha->chip_id == chip_8001) {
+ number_of_intr = 1;
+ flag |= IRQF_DISABLED;
+ } else {
+ number_of_intr = PM8001_MAX_MSIX_VEC;
+ flag &= ~IRQF_SHARED;
+ flag |= IRQF_DISABLED;
+ }
+
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) {
+ PM8001_INIT_DBG(pm8001_ha, pm8001_printk(
+ "pci_enable_msix request ret:%d no of intr %d\n",
+ rc, pm8001_ha->number_of_intr));
+
+ for (i = 0; i < number_of_intr; i++)
+ pm8001_ha->outq[i] = i;
+
for (i = 0; i < number_of_intr; i++) {
+ snprintf(intr_drvname[i], sizeof(intr_drvname[0]),
+ DRV_NAME"%d", i);
if (request_irq(pm8001_ha->msix_entries[i].vector,
- irq_handler, flag, DRV_NAME,
- SHOST_TO_SAS_HA(pm8001_ha->shost))) {
+ pm8001_interrupt_handler_msix, flag,
+ intr_drvname[i], &pm8001_ha->outq[i])) {
for (j = 0; j < i; j++)
free_irq(
pm8001_ha->msix_entries[j].vector,
- SHOST_TO_SAS_HA(pm8001_ha->shost));
+ &pm8001_ha->outq[j]);
pci_disable_msix(pm8001_ha->pdev);
break;
}
@@ -588,22 +729,24 @@ static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha,
static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha)
{
struct pci_dev *pdev;
- irq_handler_t irq_handler = pm8001_interrupt;
int rc;
pdev = pm8001_ha->pdev;
#ifdef PM8001_USE_MSIX
if (pci_find_capability(pdev, PCI_CAP_ID_MSIX))
- return pm8001_setup_msix(pm8001_ha, irq_handler);
- else
+ return pm8001_setup_msix(pm8001_ha);
+ else {
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("MSIX not supported!!!\n"));
goto intx;
+ }
#endif
intx:
/* initialize the INT-X interrupt */
- rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, DRV_NAME,
- SHOST_TO_SAS_HA(pm8001_ha->shost));
+ rc = request_irq(pdev->irq, pm8001_interrupt_handler_intx, IRQF_SHARED,
+ DRV_NAME, SHOST_TO_SAS_HA(pm8001_ha->shost));
return rc;
}
@@ -621,12 +764,13 @@ static int pm8001_pci_probe(struct pci_dev *pdev,
{
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;
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;
@@ -665,25 +809,39 @@ static int 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_FAIL_DBG(pm8001_ha, pm8001_printk(
+ "chip_init failed [ret: %d]\n", rc));
goto err_out_ha_free;
+ }
rc = scsi_add_host(shost, &pdev->dev);
if (rc)
goto err_out_ha_free;
rc = pm8001_request_irq(pm8001_ha);
- if (rc)
+ if (rc) {
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk(
+ "pm8001_request_irq failed [ret: %d]\n", rc));
goto err_out_shost;
+ }
+
+ 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);
+ }
- 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));
@@ -719,14 +877,15 @@ static void pm8001_pci_remove(struct pci_dev *pdev)
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);
+ PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0xFF);
+ PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha);
#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);
+ free_irq(pm8001_ha->msix_entries[i].vector,
+ &pm8001_ha->outq[i]);
pci_disable_msix(pdev);
#else
free_irq(pm8001_ha->irq, sha);
@@ -763,13 +922,14 @@ static int pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state)
printk(KERN_ERR " PCI PM not supported\n");
return -ENODEV;
}
- PM8001_CHIP_DISP->interrupt_disable(pm8001_ha);
- PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
+ PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0xFF);
+ PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha);
#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);
+ free_irq(pm8001_ha->msix_entries[i].vector,
+ &pm8001_ha->outq[i]);
pci_disable_msix(pdev);
#else
free_irq(pm8001_ha->irq, sha);
@@ -798,6 +958,7 @@ static int pm8001_pci_resume(struct pci_dev *pdev)
struct sas_ha_struct *sha = pci_get_drvdata(pdev);
struct pm8001_hba_info *pm8001_ha;
int rc;
+ u8 i = 0;
u32 device_state;
pm8001_ha = sha->lldd_ha;
device_state = pdev->current_state;
@@ -820,19 +981,33 @@ static int pm8001_pci_resume(struct pci_dev *pdev)
if (rc)
goto err_out_disable;
- PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
+ /* chip soft rst only for spc */
+ if (pm8001_ha->chip_id == chip_8001) {
+ PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("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
+#ifdef PM8001_USE_TASKLET
+ /* default tasklet for non msi-x interrupt handler/first msi-x
+ * interrupt handler */
tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet,
- (unsigned long)pm8001_ha);
- #endif
- PM8001_CHIP_DISP->interrupt_enable(pm8001_ha);
+ (unsigned long)pm8001_ha);
+#endif
+ 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);
+ }
scsi_unblock_requests(pm8001_ha->shost);
return 0;
@@ -843,14 +1018,45 @@ err_out_enable:
return rc;
}
+/* update of pci device, vendor id and driver data with
+ * unique value for each of the controller
+ */
static struct pci_device_id pm8001_pci_table[] = {
- {
- PCI_VDEVICE(PMC_Sierra, 0x8001), chip_8001
- },
+ { PCI_VDEVICE(PMC_Sierra, 0x8001), chip_8001 },
{
PCI_DEVICE(0x117c, 0x0042),
.driver_data = 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_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 },
{} /* terminate list */
};
@@ -870,7 +1076,7 @@ static int __init pm8001_init(void)
{
int rc = -ENOMEM;
- pm8001_wq = alloc_workqueue("pm8001", 0, 0);
+ pm8001_wq = alloc_workqueue("pm80xx", 0, 0);
if (!pm8001_wq)
goto err;
@@ -902,7 +1108,8 @@ 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_DESCRIPTION(
+ "PMC-Sierra PM8001/8081/8088/8089 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 b961112395d5..a85d73de7c80 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.
@@ -68,7 +68,7 @@ static void pm8001_tag_clear(struct pm8001_hba_info *pm8001_ha, u32 tag)
clear_bit(tag, bitmap);
}
-static void pm8001_tag_free(struct pm8001_hba_info *pm8001_ha, u32 tag)
+void pm8001_tag_free(struct pm8001_hba_info *pm8001_ha, u32 tag)
{
pm8001_tag_clear(pm8001_ha, tag);
}
@@ -212,10 +212,12 @@ int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
break;
case PHY_FUNC_GET_EVENTS:
spin_lock_irqsave(&pm8001_ha->lock, flags);
- if (-1 == pm8001_bar4_shift(pm8001_ha,
+ 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;
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ return -EINVAL;
+ }
}
{
struct sas_phy *phy = sas_phy->phy;
@@ -228,7 +230,8 @@ int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
phy->loss_of_dword_sync_count = qp[3];
phy->phy_reset_problem_count = qp[4];
}
- pm8001_bar4_shift(pm8001_ha, 0);
+ if (pm8001_ha->chip_id == chip_8001)
+ pm8001_bar4_shift(pm8001_ha, 0);
spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return 0;
default:
@@ -249,7 +252,9 @@ void pm8001_scan_start(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;
- PM8001_CHIP_DISP->sas_re_init_req(pm8001_ha);
+ /* 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_CHIP_DISP->phy_start_req(pm8001_ha, i);
}
@@ -352,7 +357,7 @@ static int sas_find_local_port_id(struct domain_device *dev)
* @tmf: the task management IU
*/
#define DEV_IS_GONE(pm8001_dev) \
- ((!pm8001_dev || (pm8001_dev->dev_type == NO_DEVICE)))
+ ((!pm8001_dev || (pm8001_dev->dev_type == SAS_PHY_UNUSED)))
static int pm8001_task_exec(struct sas_task *task, const int num,
gfp_t gfp_flags, int is_tmf, struct pm8001_tmf_task *tmf)
{
@@ -370,7 +375,7 @@ static int pm8001_task_exec(struct sas_task *task, const int num,
struct task_status_struct *tsm = &t->task_status;
tsm->resp = SAS_TASK_UNDELIVERED;
tsm->stat = SAS_PHY_DOWN;
- if (dev->dev_type != SATA_DEV)
+ if (dev->dev_type != SAS_SATA_DEV)
t->task_done(t);
return 0;
}
@@ -548,7 +553,7 @@ struct pm8001_device *pm8001_alloc_dev(struct pm8001_hba_info *pm8001_ha)
{
u32 dev;
for (dev = 0; dev < PM8001_MAX_DEVICES; dev++) {
- if (pm8001_ha->devices[dev].dev_type == NO_DEVICE) {
+ if (pm8001_ha->devices[dev].dev_type == SAS_PHY_UNUSED) {
pm8001_ha->devices[dev].id = dev;
return &pm8001_ha->devices[dev];
}
@@ -560,13 +565,31 @@ struct pm8001_device *pm8001_alloc_dev(struct pm8001_hba_info *pm8001_ha)
}
return NULL;
}
+/**
+ * pm8001_find_dev - find a matching pm8001_device
+ * @pm8001_ha: our hba card information
+ */
+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].device_id == device_id)
+ return &pm8001_ha->devices[dev];
+ }
+ if (dev == PM8001_MAX_DEVICES) {
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("NO MATCHING "
+ "DEVICE FOUND !!!\n"));
+ }
+ return NULL;
+}
static 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;
}
@@ -624,7 +647,7 @@ static int pm8001_dev_found_notify(struct domain_device *dev)
res = -1;
}
} 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*/
@@ -634,7 +657,7 @@ static int pm8001_dev_found_notify(struct domain_device *dev)
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;
return 0;
@@ -648,7 +671,7 @@ int pm8001_dev_found(struct domain_device *dev)
return pm8001_dev_found_notify(dev);
}
-static void pm8001_task_done(struct sas_task *task)
+void pm8001_task_done(struct sas_task *task)
{
if (!del_timer(&task->slow_task->timer))
return;
@@ -904,7 +927,7 @@ void pm8001_open_reject_retry(
struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[i];
pm8001_dev = ccb->device;
- if (!pm8001_dev || (pm8001_dev->dev_type == NO_DEVICE))
+ if (!pm8001_dev || (pm8001_dev->dev_type == SAS_PHY_UNUSED))
continue;
if (!device_to_close) {
uintptr_t d = (uintptr_t)pm8001_dev
@@ -995,6 +1018,72 @@ int pm8001_I_T_nexus_reset(struct domain_device *dev)
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;
+ u32 device_id = 0;
+
+ if (!dev || !dev->lldd_dev)
+ return -1;
+
+ pm8001_dev = dev->lldd_dev;
+ device_id = pm8001_dev->device_id;
+ pm8001_ha = pm8001_find_ha_by_dev(dev);
+
+ PM8001_EH_DBG(pm8001_ha,
+ pm8001_printk("I_T_Nexus handler invoked !!"));
+
+ phy = sas_get_local_phy(dev);
+
+ if (dev_is_sata(dev)) {
+ DECLARE_COMPLETION_ONSTACK(completion_setstate);
+ if (scsi_is_sas_phy_local(phy)) {
+ rc = 0;
+ goto out;
+ }
+ /* send internal ssp/sata/smp abort command to FW */
+ rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
+ dev, 1, 0);
+ 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_dev->setds_completion = &completion_setstate;
+
+ wait_for_completion(&completion_setstate);
+ } else {
+ /* send internal ssp/sata/smp abort command to FW */
+ rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
+ dev, 1, 0);
+ 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));
+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)
{
diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h
index 11008205aeb3..570819464d90 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.
@@ -57,8 +57,8 @@
#include <linux/atomic.h>
#include "pm8001_defs.h"
-#define DRV_NAME "pm8001"
-#define DRV_VERSION "0.1.36"
+#define DRV_NAME "pm80xx"
+#define DRV_VERSION "0.1.37"
#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,8 +66,8 @@
#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_printk(format, arg...) printk(KERN_INFO "pm80xx %s %d:" \
+ format, __func__, __LINE__, ## arg)
#define PM8001_CHECK_LOGGING(HBA, LEVEL, CMD) \
do { \
if (unlikely(HBA->logging_level & LEVEL)) \
@@ -103,11 +103,12 @@ do { \
#define PM8001_READ_VPD
-#define DEV_IS_EXPANDER(type) ((type == EDGE_DEV) || (type == FANOUT_DEV))
+#define DEV_IS_EXPANDER(type) ((type == SAS_EDGE_EXPANDER_DEVICE) || (type == SAS_FANOUT_EXPANDER_DEVICE))
#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;
struct pm8001_hba_info;
struct pm8001_ccb_info;
@@ -131,15 +132,15 @@ struct pm8001_ioctl_payload {
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);
+ 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);
+ irqreturn_t (*isr)(struct pm8001_hba_info *pm8001_ha, u8 vec);
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);
+ 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);
@@ -173,6 +174,7 @@ struct pm8001_dispatch {
};
struct pm8001_chip_info {
+ u32 encrypt;
u32 n_phy;
const struct pm8001_dispatch *dispatch;
};
@@ -204,7 +206,7 @@ struct pm8001_phy {
};
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;
@@ -256,7 +258,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;
@@ -292,19 +307,69 @@ 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;
+ } 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;
@@ -351,15 +416,21 @@ struct pm8001_hba_info {
struct device *dev;
struct pm8001_hba_memspace io_mem[6];
struct mpi_mem_req memoryMap;
+ struct encrypt encrypt_info; /* support encryption */
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;
- struct inbound_queue_table inbnd_q_tbl[PM8001_MAX_INB_NUM];
- struct outbound_queue_table outbnd_q_tbl[PM8001_MAX_OUTB_NUM];
+ void __iomem *pspa_q_tbl_addr;
+ /*MPI SAS PHY attributes Queue Config Table Addr*/
+ void __iomem *ivt_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_SPCV_INB_NUM];
+ struct outbound_queue_table outbnd_q_tbl[PM8001_MAX_SPCV_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;
@@ -372,10 +443,12 @@ struct pm8001_hba_info {
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*/
+ struct msix_entry msix_entries[PM8001_MAX_MSIX_VEC];
+ /*for msi-x interrupt*/
int number_of_intr;/*will be used in remove()*/
#endif
#ifdef PM8001_USE_TASKLET
@@ -383,7 +456,10 @@ struct pm8001_hba_info {
#endif
u32 logging_level;
u32 fw_status;
+ u32 smp_exp_mode;
+ u32 int_vector;
const struct firmware *fw_image;
+ u8 outq[PM8001_MAX_MSIX_VEC];
};
struct pm8001_work {
@@ -419,6 +495,9 @@ struct pm8001_fw_image_header {
#define FLASH_UPDATE_DNLD_NOT_SUPPORTED 0x10
#define FLASH_UPDATE_DISABLED 0x11
+#define NCQ_READ_LOG_FLAG 0x80000000
+#define NCQ_ABORT_ALL_FLAG 0x40000000
+#define NCQ_2ND_RLE_FLAG 0x20000000
/**
* brief param structure for firmware flash update.
*/
@@ -484,6 +563,7 @@ 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_open_reject_retry(
struct pm8001_hba_info *pm8001_ha,
@@ -493,6 +573,61 @@ 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,
+ struct inbound_queue_table *circularQ,
+ u32 opCode, void *payload, 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 pm8001_tmf_task *tmf);
+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_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);
+struct sas_task *pm8001_alloc_task(void);
+void pm8001_task_done(struct sas_task *task);
+void pm8001_free_task(struct sas_task *task);
+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);
/* ctl shared API */
diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c
new file mode 100644
index 000000000000..302514d8157b
--- /dev/null
+++ b/drivers/scsi/pm8001/pm80xx_hwi.c
@@ -0,0 +1,4130 @@
+/*
+ * 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"
+
+#define SMP_DIRECT 1
+#define SMP_INDIRECT 2
+/**
+ * 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_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_SPCV_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_SPCV_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;
+
+ 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 = 0x01;
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_interrupt = 0x01;
+
+ /* Disable end to end CRC checking */
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.crc_core_dump = (0x1 << 16);
+
+ for (i = 0; i < PM8001_MAX_SPCV_INB_NUM; i++) {
+ pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt =
+ PM8001_MPI_QUEUE | (64 << 16) | (0x00<<30);
+ pm8001_ha->inbnd_q_tbl[i].upper_base_addr =
+ pm8001_ha->memoryMap.region[IB + i].phys_addr_hi;
+ pm8001_ha->inbnd_q_tbl[i].lower_base_addr =
+ pm8001_ha->memoryMap.region[IB + i].phys_addr_lo;
+ pm8001_ha->inbnd_q_tbl[i].base_virt =
+ (u8 *)pm8001_ha->memoryMap.region[IB + i].virt_ptr;
+ pm8001_ha->inbnd_q_tbl[i].total_length =
+ pm8001_ha->memoryMap.region[IB + i].total_len;
+ pm8001_ha->inbnd_q_tbl[i].ci_upper_base_addr =
+ pm8001_ha->memoryMap.region[CI + i].phys_addr_hi;
+ pm8001_ha->inbnd_q_tbl[i].ci_lower_base_addr =
+ pm8001_ha->memoryMap.region[CI + i].phys_addr_lo;
+ pm8001_ha->inbnd_q_tbl[i].ci_virt =
+ pm8001_ha->memoryMap.region[CI + i].virt_ptr;
+ 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;
+ }
+ for (i = 0; i < PM8001_MAX_SPCV_OUTB_NUM; i++) {
+ pm8001_ha->outbnd_q_tbl[i].element_size_cnt =
+ PM8001_MPI_QUEUE | (64 << 16) | (0x01<<30);
+ pm8001_ha->outbnd_q_tbl[i].upper_base_addr =
+ pm8001_ha->memoryMap.region[OB + i].phys_addr_hi;
+ pm8001_ha->outbnd_q_tbl[i].lower_base_addr =
+ pm8001_ha->memoryMap.region[OB + i].phys_addr_lo;
+ pm8001_ha->outbnd_q_tbl[i].base_virt =
+ (u8 *)pm8001_ha->memoryMap.region[OB + i].virt_ptr;
+ pm8001_ha->outbnd_q_tbl[i].total_length =
+ pm8001_ha->memoryMap.region[OB + i].total_len;
+ pm8001_ha->outbnd_q_tbl[i].pi_upper_base_addr =
+ pm8001_ha->memoryMap.region[PI + i].phys_addr_hi;
+ pm8001_ha->outbnd_q_tbl[i].pi_lower_base_addr =
+ pm8001_ha->memoryMap.region[PI + 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 + i].virt_ptr;
+ 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;
+ }
+}
+
+/**
+ * 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);
+ pm8001_mw32(address, MAIN_FATAL_ERROR_INTERRUPT,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_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_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);
+}
+
+/**
+ * update_inbnd_queue_table - update the inbound queue table to the HBA.
+ * @pm8001_ha: our hba card information
+ */
+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);
+}
+
+/**
+ * update_outbnd_queue_table - update the outbound queue table to the HBA.
+ * @pm8001_ha: our hba card information
+ */
+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);
+}
+
+/**
+ * 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 */
+ max_wait_count = 2 * 1000 * 1000;/* 2 sec for spcv/ve */
+ do {
+ udelay(1);
+ 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)
+ return -1;
+ /* check the MPI-State for initialization upto 100ms*/
+ max_wait_count = 100 * 1000;/* 100 msec */
+ do {
+ udelay(1);
+ 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 -1;
+
+ /* check MPI Initialization error */
+ gst_len_mpistate = gst_len_mpistate >> 16;
+ if (0x0000 != gst_len_mpistate)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * check_fw_ready - The LLDD check if the FW is ready, if not, return error.
+ * @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;
+ int ret = 0;
+
+ /* reset / PCIe ready */
+ max_wait_time = max_wait_count = 100 * 1000; /* 100 milli sec */
+ do {
+ udelay(1);
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ } while ((value == 0xFFFFFFFF) && (--max_wait_count));
+
+ /* check ila status */
+ max_wait_time = max_wait_count = 1000 * 1000; /* 1000 milli sec */
+ do {
+ udelay(1);
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ } while (((value & SCRATCH_PAD_ILA_READY) !=
+ SCRATCH_PAD_ILA_READY) && (--max_wait_count));
+ if (!max_wait_count)
+ ret = -1;
+ else {
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" ila ready status in %d millisec\n",
+ (max_wait_time - max_wait_count)));
+ }
+
+ /* check RAAE status */
+ max_wait_time = max_wait_count = 1800 * 1000; /* 1800 milli sec */
+ do {
+ udelay(1);
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ } while (((value & SCRATCH_PAD_RAAE_READY) !=
+ SCRATCH_PAD_RAAE_READY) && (--max_wait_count));
+ if (!max_wait_count)
+ ret = -1;
+ else {
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" raae ready status in %d millisec\n",
+ (max_wait_time - max_wait_count)));
+ }
+
+ /* check iop0 status */
+ max_wait_time = max_wait_count = 600 * 1000; /* 600 milli sec */
+ do {
+ udelay(1);
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ } while (((value & SCRATCH_PAD_IOP0_READY) != SCRATCH_PAD_IOP0_READY) &&
+ (--max_wait_count));
+ if (!max_wait_count)
+ ret = -1;
+ else {
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" iop0 ready status in %d millisec\n",
+ (max_wait_time - max_wait_count)));
+ }
+
+ /* check iop1 status only for 16 port controllers */
+ if ((pm8001_ha->chip_id != chip_8008) &&
+ (pm8001_ha->chip_id != chip_8009)) {
+ /* 200 milli sec */
+ max_wait_time = max_wait_count = 200 * 1000;
+ do {
+ udelay(1);
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ } while (((value & SCRATCH_PAD_IOP1_READY) !=
+ SCRATCH_PAD_IOP1_READY) && (--max_wait_count));
+ if (!max_wait_count)
+ ret = -1;
+ else {
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
+ "iop1 ready status in %d millisec\n",
+ (max_wait_time - max_wait_count)));
+ }
+ }
+
+ return ret;
+}
+
+static void 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);
+ offset = value & 0x03FFFFFF; /* scratch pad 0 TBL address */
+
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Scratchpad 0 Offset: 0x%x value 0x%x\n",
+ offset, value));
+ 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_ha->main_cfg_tbl_addr = base_addr =
+ pm8001_ha->io_mem[pcibar].memvirtaddr + offset;
+ 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_INIT_DBG(pm8001_ha,
+ pm8001_printk("GST OFFSET 0x%x\n",
+ pm8001_cr32(pm8001_ha, pcibar, offset + 0x18)));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("INBND OFFSET 0x%x\n",
+ pm8001_cr32(pm8001_ha, pcibar, offset + 0x1C)));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("OBND OFFSET 0x%x\n",
+ pm8001_cr32(pm8001_ha, pcibar, offset + 0x20)));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("IVT OFFSET 0x%x\n",
+ pm8001_cr32(pm8001_ha, pcibar, offset + 0x8C)));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("PSPA OFFSET 0x%x\n",
+ pm8001_cr32(pm8001_ha, pcibar, offset + 0x90)));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("addr - main cfg %p general status %p\n",
+ pm8001_ha->main_cfg_tbl_addr,
+ pm8001_ha->general_stat_tbl_addr));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("addr - inbnd %p obnd %p\n",
+ pm8001_ha->inbnd_q_tbl_addr,
+ pm8001_ha->outbnd_q_tbl_addr));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("addr - pspa %p ivt %p\n",
+ pm8001_ha->pspa_q_tbl_addr,
+ pm8001_ha->ivt_tbl_addr));
+}
+
+/**
+ * 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;
+ struct inbound_queue_table *circularQ;
+ int rc;
+ u32 tag;
+ u32 opc = OPC_INB_SET_CONTROLLER_CONFIG;
+
+ memset(&payload, 0, sizeof(struct set_ctrl_cfg_req));
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return -1;
+
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ payload.tag = cpu_to_le32(tag);
+ payload.cfg_pg[0] = (THERMAL_LOG_ENABLE << 9) |
+ (THERMAL_ENABLE << 8) | THERMAL_OP_CODE;
+ payload.cfg_pg[1] = (LTEMPHIL << 24) | (RTEMPHIL << 8);
+
+ rc = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
+ 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;
+ struct inbound_queue_table *circularQ;
+ 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 -1;
+
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ payload.tag = cpu_to_le32(tag);
+
+ SASConfigPage.pageCode = SAS_PROTOCOL_TIMER_CONFIG_PAGE;
+ SASConfigPage.MST_MSI = 3 << 15;
+ SASConfigPage.STP_SSP_MCT_TMO = (STP_MCT_TMO << 16) | SSP_MCT_TMO;
+ SASConfigPage.STP_FRM_TMO = (SAS_MAX_OPEN_TIME << 24) |
+ (SMP_MAX_CONN_TIMER << 16) | STP_FRM_TIMER;
+ SASConfigPage.STP_IDLE_TMO = STP_IDLE_TIME;
+
+ if (SASConfigPage.STP_IDLE_TMO > 0x3FFFFFF)
+ SASConfigPage.STP_IDLE_TMO = 0x3FFFFFF;
+
+
+ SASConfigPage.OPNRJT_RTRY_INTVL = (SAS_MFD << 16) |
+ SAS_OPNRJT_RTRY_INTVL;
+ SASConfigPage.Data_Cmd_OPNRJT_RTRY_TMO = (SAS_DOPNRJT_RTRY_TMO << 16)
+ | SAS_COPNRJT_RTRY_TMO;
+ SASConfigPage.Data_Cmd_OPNRJT_RTRY_THR = (SAS_DOPNRJT_RTRY_THR << 16)
+ | SAS_COPNRJT_RTRY_THR;
+ SASConfigPage.MAX_AIP = SAS_MAX_AIP;
+
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("SASConfigPage.pageCode "
+ "0x%08x\n", SASConfigPage.pageCode));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("SASConfigPage.MST_MSI "
+ " 0x%08x\n", SASConfigPage.MST_MSI));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("SASConfigPage.STP_SSP_MCT_TMO "
+ " 0x%08x\n", SASConfigPage.STP_SSP_MCT_TMO));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("SASConfigPage.STP_FRM_TMO "
+ " 0x%08x\n", SASConfigPage.STP_FRM_TMO));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("SASConfigPage.STP_IDLE_TMO "
+ " 0x%08x\n", SASConfigPage.STP_IDLE_TMO));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("SASConfigPage.OPNRJT_RTRY_INTVL "
+ " 0x%08x\n", SASConfigPage.OPNRJT_RTRY_INTVL));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("SASConfigPage.Data_Cmd_OPNRJT_RTRY_TMO "
+ " 0x%08x\n", SASConfigPage.Data_Cmd_OPNRJT_RTRY_TMO));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("SASConfigPage.Data_Cmd_OPNRJT_RTRY_THR "
+ " 0x%08x\n", SASConfigPage.Data_Cmd_OPNRJT_RTRY_THR));
+ PM8001_INIT_DBG(pm8001_ha, pm8001_printk("SASConfigPage.MAX_AIP "
+ " 0x%08x\n", SASConfigPage.MAX_AIP));
+
+ memcpy(&payload.cfg_pg, &SASConfigPage,
+ sizeof(SASProtocolTimerConfig_t));
+
+ rc = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
+
+ 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;
+
+ /* 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_INIT_DBG(pm8001_ha, pm8001_printk(
+ "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_INIT_DBG(pm8001_ha, pm8001_printk(
+ "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;
+ return 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_INIT_DBG(pm8001_ha, pm8001_printk(
+ "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));
+ ret = -1;
+ } 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_INIT_DBG(pm8001_ha, pm8001_printk(
+ "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));
+ ret = -1;
+ }
+ return ret;
+}
+
+/**
+ * pm80xx_encrypt_update - update flash with encryption informtion
+ * @pm8001_ha: our hba card information.
+ */
+static int pm80xx_encrypt_update(struct pm8001_hba_info *pm8001_ha)
+{
+ struct kek_mgmt_req payload;
+ struct inbound_queue_table *circularQ;
+ 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 -1;
+
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ 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 = ((1 << 24) | (1 << 16) | (1 << 8) |
+ KEK_MGMT_SUBOP_KEYCARDUPDATE);
+
+ rc = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
+
+ return rc;
+}
+
+/**
+ * pm8001_chip_init - the main init function that initialize 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_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Firmware is not ready!\n"));
+ return -EBUSY;
+ }
+
+ /* Initialize pci space address eg: mpi offset */
+ init_pci_device_addresses(pm8001_ha);
+ 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_MAX_SPCV_INB_NUM; i++)
+ update_inbnd_queue_table(pm8001_ha, i);
+ for (i = 0; i < PM8001_MAX_SPCV_OUTB_NUM; i++)
+ update_outbnd_queue_table(pm8001_ha, i);
+
+ /* 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"));
+ } else
+ return -EBUSY;
+
+ /* send SAS protocol timer configuration page to FW */
+ ret = pm80xx_set_sas_protocol_timer_config(pm8001_ha);
+
+ /* Check for encryption */
+ if (pm8001_ha->chip->encrypt) {
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Checking for encryption\n"));
+ ret = pm80xx_get_encrypt_info(pm8001_ha);
+ if (ret == -1) {
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Encryption error !!\n"));
+ if (pm8001_ha->encrypt_info.status == 0x81) {
+ PM8001_INIT_DBG(pm8001_ha, pm8001_printk(
+ "Encryption enabled with error."
+ "Saving encryption key to flash\n"));
+ pm80xx_encrypt_update(pm8001_ha);
+ }
+ }
+ }
+ return 0;
+}
+
+static int mpi_uninit_check(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 max_wait_count;
+ u32 value;
+ u32 gst_len_mpistate;
+ init_pci_device_addresses(pm8001_ha);
+ /* 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 */
+ max_wait_count = 2 * 1000 * 1000; /* 2 sec for spcv/ve */
+ do {
+ udelay(1);
+ 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_FAIL_DBG(pm8001_ha,
+ pm8001_printk("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 = 2 * 1000 * 1000; /* 2 sec for spcv/ve */
+ do {
+ udelay(1);
+ 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_FAIL_DBG(pm8001_ha,
+ pm8001_printk(" TIME OUT MPI State = 0x%x\n",
+ gst_len_mpistate & GST_MPI_STATE_MASK));
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * 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
+ */
+
+static int
+pm80xx_chip_soft_rst(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 regval;
+ u32 bootloader_state;
+
+ /* Check if MPI is in ready state to reset */
+ if (mpi_uninit_check(pm8001_ha) != 0) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("MPI state is not ready\n"));
+ return -1;
+ }
+
+ /* checked for reset register normal state; 0x0 */
+ regval = pm8001_cr32(pm8001_ha, 0, SPC_REG_SOFT_RESET);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("reset register before write : 0x%x\n", regval));
+
+ pm8001_cw32(pm8001_ha, 0, SPC_REG_SOFT_RESET, SPCv_NORMAL_RESET_VALUE);
+ mdelay(500);
+
+ regval = pm8001_cr32(pm8001_ha, 0, SPC_REG_SOFT_RESET);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("reset register after write 0x%x\n", regval));
+
+ if ((regval & SPCv_SOFT_RESET_READ_MASK) ==
+ SPCv_SOFT_RESET_NORMAL_RESET_OCCURED) {
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" soft reset successful [regval: 0x%x]\n",
+ regval));
+ } else {
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" 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_MSG_DBG(pm8001_ha, pm8001_printk(
+ "Bootloader state - HDA mode SEEPROM\n"));
+ } else if (bootloader_state ==
+ SCRATCH_PAD1_BOOTSTATE_HDA_BOOTSTRAP) {
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
+ "Bootloader state - HDA mode Bootstrap Pin\n"));
+ } else if (bootloader_state ==
+ SCRATCH_PAD1_BOOTSTATE_HDA_SOFTRESET) {
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
+ "Bootloader state - HDA mode soft reset\n"));
+ } else if (bootloader_state ==
+ SCRATCH_PAD1_BOOTSTATE_CRIT_ERROR) {
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
+ "Bootloader state-HDA mode critical error\n"));
+ }
+ return -EBUSY;
+ }
+
+ /* check the firmware status after reset */
+ if (-1 == check_fw_ready(pm8001_ha)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Firmware is not ready!\n"));
+ return -EBUSY;
+ }
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("SPCv soft reset Complete\n"));
+ return 0;
+}
+
+static void pm80xx_hw_chip_rst(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 i;
+
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("chip reset start\n"));
+
+ /* do SPCv chip reset. */
+ pm8001_cw32(pm8001_ha, 0, SPC_REG_SOFT_RESET, 0x11);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("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_INIT_DBG(pm8001_ha,
+ pm8001_printk("chip reset finished\n"));
+}
+
+/**
+ * pm8001_chip_interrupt_enable - enable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm80xx_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
+pm80xx_chip_intx_interrupt_disable(struct pm8001_hba_info *pm8001_ha)
+{
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR_CLR, ODMR_MASK_ALL);
+}
+
+/**
+ * pm8001_chip_interrupt_enable - enable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm80xx_chip_interrupt_enable(struct pm8001_hba_info *pm8001_ha, u8 vec)
+{
+#ifdef PM8001_USE_MSIX
+ u32 mask;
+ mask = (u32)(1 << vec);
+
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR_CLR, (u32)(mask & 0xFFFFFFFF));
+ return;
+#endif
+ pm80xx_chip_intx_interrupt_enable(pm8001_ha);
+
+}
+
+/**
+ * pm8001_chip_interrupt_disable- disable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm80xx_chip_interrupt_disable(struct pm8001_hba_info *pm8001_ha, u8 vec)
+{
+#ifdef PM8001_USE_MSIX
+ u32 mask;
+ if (vec == 0xFF)
+ mask = 0xFFFFFFFF;
+ else
+ mask = (u32)(1 << vec);
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, (u32)(mask & 0xFFFFFFFF));
+ return;
+#endif
+ pm80xx_chip_intx_interrupt_disable(pm8001_ha);
+}
+
+static void pm80xx_send_abort_all(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_ha_dev)
+{
+ int res;
+ u32 ccb_tag;
+ struct pm8001_ccb_info *ccb;
+ struct sas_task *task = NULL;
+ struct task_abort_req task_abort;
+ struct inbound_queue_table *circularQ;
+ u32 opc = OPC_INB_SATA_ABORT;
+ int ret;
+
+ if (!pm8001_ha_dev) {
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("dev is null\n"));
+ return;
+ }
+
+ task = sas_alloc_slow_task(GFP_ATOMIC);
+
+ if (!task) {
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("cannot "
+ "allocate task\n"));
+ return;
+ }
+
+ task->task_done = pm8001_task_done;
+
+ res = pm8001_tag_alloc(pm8001_ha, &ccb_tag);
+ if (res)
+ return;
+
+ ccb = &pm8001_ha->ccb_info[ccb_tag];
+ ccb->device = pm8001_ha_dev;
+ ccb->ccb_tag = ccb_tag;
+ ccb->task = task;
+
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+
+ memset(&task_abort, 0, sizeof(task_abort));
+ task_abort.abort_all = cpu_to_le32(1);
+ task_abort.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
+ task_abort.tag = cpu_to_le32(ccb_tag);
+
+ ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &task_abort, 0);
+
+}
+
+static void pm80xx_send_read_log(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_ha_dev)
+{
+ struct sata_start_req sata_cmd;
+ int res;
+ u32 ccb_tag;
+ struct pm8001_ccb_info *ccb;
+ struct sas_task *task = NULL;
+ struct host_to_dev_fis fis;
+ struct domain_device *dev;
+ struct inbound_queue_table *circularQ;
+ u32 opc = OPC_INB_SATA_HOST_OPSTART;
+
+ task = sas_alloc_slow_task(GFP_ATOMIC);
+
+ if (!task) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("cannot allocate task !!!\n"));
+ return;
+ }
+ task->task_done = pm8001_task_done;
+
+ res = pm8001_tag_alloc(pm8001_ha, &ccb_tag);
+ if (res) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("cannot allocate tag !!!\n"));
+ return;
+ }
+
+ /* allocate domain device by ourselves as libsas
+ * is not going to provide any
+ */
+ dev = kzalloc(sizeof(struct domain_device), GFP_ATOMIC);
+ if (!dev) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Domain device cannot be allocated\n"));
+ sas_free_task(task);
+ return;
+ } else {
+ task->dev = dev;
+ task->dev->lldd_dev = pm8001_ha_dev;
+ }
+
+ ccb = &pm8001_ha->ccb_info[ccb_tag];
+ ccb->device = pm8001_ha_dev;
+ ccb->ccb_tag = ccb_tag;
+ ccb->task = task;
+ pm8001_ha_dev->id |= NCQ_READ_LOG_FLAG;
+ pm8001_ha_dev->id |= NCQ_2ND_RLE_FLAG;
+
+ memset(&sata_cmd, 0, sizeof(sata_cmd));
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+
+ /* construct read log FIS */
+ memset(&fis, 0, sizeof(struct host_to_dev_fis));
+ fis.fis_type = 0x27;
+ fis.flags = 0x80;
+ fis.command = ATA_CMD_READ_LOG_EXT;
+ fis.lbal = 0x10;
+ fis.sector_count = 0x1;
+
+ sata_cmd.tag = cpu_to_le32(ccb_tag);
+ sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
+ sata_cmd.ncqtag_atap_dir_m_dad |= ((0x1 << 7) | (0x5 << 9));
+ memcpy(&sata_cmd.sata_fis, &fis, sizeof(struct host_to_dev_fis));
+
+ res = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &sata_cmd, 0);
+
+}
+
+/**
+ * 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 represent
+ * that he has finished the job,please check the coresponding 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_FAIL_DBG(pm8001_ha,
+ pm8001_printk("sas IO status 0x%x\n", status));
+ if (unlikely(!t || !t->lldd_task || !t->dev))
+ return;
+ ts = &t->task_status;
+ switch (status) {
+ case IO_SUCCESS:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_SUCCESS ,param = 0x%x\n",
+ param));
+ if (param == 0) {
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = 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)
+ pm8001_dev->running_req--;
+ break;
+ case IO_ABORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("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 = 0x%x\n",
+ param));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_UNDERRUN;
+ ts->residual = param;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_NO_DEVICE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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_IO_DBG(pm8001_ha,
+ pm8001_printk("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_IO_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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_IO_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ 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"));
+ 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"));
+ 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));
+ /* 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 = 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_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 {
+ 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);
+ }
+}
+
+/*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_FAIL_DBG(pm8001_ha,
+ pm8001_printk("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:0x%x, tag:0x%x, event:0x%x\n",
+ port_id, tag, event));
+ switch (event) {
+ case IO_OVERFLOW:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("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_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"));
+ 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"));
+ 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"));
+ 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"));
+ 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_IO_DBG(pm8001_ha,
+ pm8001_printk("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_IO_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_INTERNAL_CRC_ERROR:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("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_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_CMD_FRAME_ISSUED\n"));
+ return;
+ default:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("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_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);
+ } 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);
+ }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void
+mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct sas_task *t;
+ struct pm8001_ccb_info *ccb;
+ u32 param;
+ u32 status;
+ u32 tag;
+ 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);
+ tag = le32_to_cpu(psataPayload->tag);
+
+ if (!tag) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("tag null\n"));
+ return;
+ }
+ ccb = &pm8001_ha->ccb_info[tag];
+ param = le32_to_cpu(psataPayload->param);
+ if (ccb) {
+ t = ccb->task;
+ pm8001_dev = ccb->device;
+ } else {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("ccb null\n"));
+ return;
+ }
+
+ if (t) {
+ if (t->dev && (t->dev->lldd_dev))
+ pm8001_dev = t->dev->lldd_dev;
+ } else {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("task null\n"));
+ return;
+ }
+
+ if ((pm8001_dev && !(pm8001_dev->id & NCQ_READ_LOG_FLAG))
+ && unlikely(!t || !t->lldd_task || !t->dev)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("task or dev null\n"));
+ return;
+ }
+
+ ts = &t->task_status;
+ if (!ts) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("ts null\n"));
+ return;
+ }
+
+ switch (status) {
+ case IO_SUCCESS:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n"));
+ if (param == 0) {
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_STAT_GOOD;
+ /* check if response is for SEND READ LOG */
+ if (pm8001_dev &&
+ (pm8001_dev->id & NCQ_READ_LOG_FLAG)) {
+ /* set new bit for abort_all */
+ pm8001_dev->id |= NCQ_ABORT_ALL_FLAG;
+ /* clear bit for read log */
+ pm8001_dev->id = pm8001_dev->id & 0x7FFFFFFF;
+ pm80xx_send_abort_all(pm8001_ha, pm8001_dev);
+ /* Free the tag */
+ pm8001_tag_free(pm8001_ha, tag);
+ sas_free_task(t);
+ return;
+ }
+ } 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));
+ 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) {
+ 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) {
+ len = sizeof(struct set_dev_bits_fis);
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("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));
+ }
+ 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"));
+ }
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_ABORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_ABORTED IOMB Tag\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_ABORTED_TASK;
+ if (pm8001_dev)
+ 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));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_UNDERRUN;
+ ts->residual = param;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_NO_DEVICE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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_IO_DBG(pm8001_ha,
+ pm8001_printk("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;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/*in order to force CPU ordering*/
+ spin_unlock_irq(&pm8001_ha->lock);
+ t->task_done(t);
+ spin_lock_irq(&pm8001_ha->lock);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("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;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/*ditto*/
+ spin_unlock_irq(&pm8001_ha->lock);
+ t->task_done(t);
+ spin_lock_irq(&pm8001_ha->lock);
+ 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"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk(
+ "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;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/* ditto*/
+ spin_unlock_irq(&pm8001_ha->lock);
+ t->task_done(t);
+ spin_lock_irq(&pm8001_ha->lock);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_ABORTED_TASK;
+ break;
+ case IO_XFER_ERROR_SATA_LINK_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_SATA_LINK_TIMEOUT\n"));
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ case IO_XFER_ERROR_REJECTED_NCQ_MODE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_PORT_IN_RESET:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_PORT_IN_RESET\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ case IO_DS_NON_OPERATIONAL:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("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;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/*ditto*/
+ spin_unlock_irq(&pm8001_ha->lock);
+ t->task_done(t);
+ spin_lock_irq(&pm8001_ha->lock);
+ return;
+ }
+ break;
+ case IO_DS_IN_RECOVERY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_DS_IN_RECOVERY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ case IO_DS_IN_ERROR:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("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;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/*ditto*/
+ spin_unlock_irq(&pm8001_ha->lock);
+ t->task_done(t);
+ spin_lock_irq(&pm8001_ha->lock);
+ 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"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ default:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("Unknown status 0x%x\n", status));
+ /* not allowed case. Therefore, return failed status */
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ 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_irq(&pm8001_ha->lock);
+ t->task_done(t);
+ spin_lock_irq(&pm8001_ha->lock);
+ } 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_irq(&pm8001_ha->lock);
+ t->task_done(t);
+ spin_lock_irq(&pm8001_ha->lock);
+ }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , 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);
+ unsigned long flags;
+
+ ccb = &pm8001_ha->ccb_info[tag];
+
+ if (ccb) {
+ t = ccb->task;
+ pm8001_dev = ccb->device;
+ } else {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("No CCB !!!. returning\n"));
+ return;
+ }
+ if (event)
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("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);
+ /* send read log extension */
+ if (pm8001_dev)
+ pm80xx_send_read_log(pm8001_ha, pm8001_dev);
+ return;
+ }
+
+ if (unlikely(!t || !t->lldd_task || !t->dev)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("task or dev null\n"));
+ return;
+ }
+
+ ts = &t->task_status;
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("port_id:0x%x, tag:0x%x, event:0x%x\n",
+ port_id, tag, event));
+ switch (event) {
+ case IO_OVERFLOW:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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_FAIL_DBG(pm8001_ha,
+ pm8001_printk("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;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/*ditto*/
+ spin_unlock_irq(&pm8001_ha->lock);
+ t->task_done(t);
+ spin_lock_irq(&pm8001_ha->lock);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ break;
+ case IO_XFER_PIO_SETUP_ERROR:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("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_FAIL_DBG(pm8001_ha,
+ pm8001_printk("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_FAIL_DBG(pm8001_ha,
+ pm8001_printk("IO_XFR_DMA_ACTIVATE_TIMEOUT\n"));
+ /* TBC: used default set values */
+ 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));
+ /* 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_irq(&pm8001_ha->lock);
+ t->task_done(t);
+ spin_lock_irq(&pm8001_ha->lock);
+ } 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_irq(&pm8001_ha->lock);
+ t->task_done(t);
+ spin_lock_irq(&pm8001_ha->lock);
+ }
+}
+
+/*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;
+ char *pdma_respaddr = NULL;
+
+ 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_FAIL_DBG(pm8001_ha,
+ pm8001_printk("smp IO status 0x%x\n", status));
+ if (unlikely(!t || !t->lldd_task || !t->dev))
+ return;
+
+ switch (status) {
+
+ case IO_SUCCESS:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_STAT_GOOD;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ if (pm8001_ha->smp_exp_mode == SMP_DIRECT) {
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("DIRECT RESPONSE Length:%d\n",
+ param));
+ pdma_respaddr = (char *)(phys_to_virt(cpu_to_le64
+ ((u64)sg_dma_address
+ (&t->smp_task.smp_resp))));
+ for (i = 0; i < param; i++) {
+ *(pdma_respaddr+i) = psmpPayload->_r_a[i];
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk(
+ "SMP Byte%d DMA data 0x%x psmp 0x%x\n",
+ i, *(pdma_respaddr+i),
+ psmpPayload->_r_a[i]));
+ }
+ }
+ break;
+ case IO_ABORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_ABORTED IOMB\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_ABORTED_TASK;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_OVERFLOW:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("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_NO_DEVICE:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("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"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_STAT_BUSY;
+ 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 = 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"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = 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"));
+ 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"));
+ 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"));
+ 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_IO_DBG(pm8001_ha,
+ pm8001_printk("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_IO_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ 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));
+ 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_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 {
+ 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);
+ }
+}
+
+/**
+ * pm80xx_hw_event_ack_req- For PM8001,some events need to acknowage 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;
+
+ struct inbound_queue_table *circularQ;
+
+ memset((u8 *)&payload, 0, sizeof(payload));
+ circularQ = &pm8001_ha->inbnd_q_tbl[Qnum];
+ 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, circularQ, opc, &payload, 0);
+}
+
+static int pm80xx_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha,
+ u32 phyId, u32 phy_op);
+
+/**
+ * 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 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;
+ port->port_state = portstate;
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
+ "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_MSG_DBG(pm8001_ha,
+ pm8001_printk("device type no device.\n"));
+ break;
+ case SAS_END_DEVICE:
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("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_MSG_DBG(pm8001_ha,
+ pm8001_printk("expander device.\n"));
+ port->port_attached = 1;
+ pm8001_get_lrate_mode(phy, link_rate);
+ break;
+ case SAS_FANOUT_EXPANDER_DEVICE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("fanout expander device.\n"));
+ port->port_attached = 1;
+ pm8001_get_lrate_mode(phy, link_rate);
+ break;
+ default:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE);
+ 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 disk to spinup*/
+ 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 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(
+ "port id %d, phy id %d link_rate %d portstate 0x%x\n",
+ port_id, phy_id, link_rate, portstate));
+
+ port->port_state = portstate;
+ 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_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE);
+ 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];
+ port->port_state = portstate;
+ phy->phy_type = 0;
+ phy->identify.device_type = 0;
+ phy->phy_attached = 0;
+ memset(&phy->dev_sas_addr, 0, SAS_ADDR_SIZE);
+ switch (portstate) {
+ 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"));
+ port->port_attached = 0;
+ pm80xx_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));
+ break;
+ case PORT_NOT_ESTABLISHED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" 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"));
+ port->port_attached = 0;
+ pm80xx_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) = 0x%x\n",
+ portstate));
+ break;
+
+ }
+}
+
+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);
+ struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("phy start resp status:0x%x, phyid:0x%x\n",
+ status, phy_id));
+ if (status == 0) {
+ phy->phy_state = 1;
+ if (pm8001_ha->flags == PM8001F_RUN_TIME)
+ complete(phy->enable_completion);
+ }
+ return 0;
+
+}
+
+/**
+ * mpi_thermal_hw_event -The 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_IO_DBG(pm8001_ha, pm8001_printk(
+ "Thermal Event: Local high temperature violated!\n"));
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk(
+ "Thermal Event: Measured local high temperature %d\n",
+ ((rht_lht & 0xFF00) >> 8)));
+ }
+ if (thermal_event & 0x10) {
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk(
+ "Thermal Event: Remote high temperature violated!\n"));
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk(
+ "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;
+ 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);
+ 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 asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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_MSG_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ hw_event_sata_phy_up(pm8001_ha, piomb);
+ 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);
+ 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);
+ 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"));
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ 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"));
+ 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_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+ break;
+ case HW_EVENT_PHY_ERROR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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);
+ break;
+ case HW_EVENT_BROADCAST_EXP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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);
+ break;
+ case HW_EVENT_LINK_ERR_INVALID_DWORD:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_LINK_ERR_INVALID_DWORD\n"));
+ pm80xx_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);
+ break;
+ case HW_EVENT_LINK_ERR_DISPARITY_ERROR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_LINK_ERR_DISPARITY_ERROR\n"));
+ pm80xx_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);
+ break;
+ case HW_EVENT_LINK_ERR_CODE_VIOLATION:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_LINK_ERR_CODE_VIOLATION\n"));
+ pm80xx_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);
+ 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"));
+ pm80xx_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);
+ break;
+ case HW_EVENT_MALFUNCTION:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_MALFUNCTION\n"));
+ break;
+ case HW_EVENT_BROADCAST_SES:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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);
+ break;
+ case HW_EVENT_INBOUND_CRC_ERROR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_INBOUND_CRC_ERROR\n"));
+ 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_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_HARD_RESET_RECEIVED\n"));
+ sas_ha->notify_port_event(sas_phy, PORTE_HARD_RESET);
+ break;
+ case HW_EVENT_ID_FRAME_TIMEOUT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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);
+ break;
+ case HW_EVENT_LINK_ERR_PHY_RESET_FAILED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_LINK_ERR_PHY_RESET_FAILED\n"));
+ 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_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_PORT_RESET_TIMER_TMO:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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);
+ break;
+ case HW_EVENT_PORT_RECOVERY_TIMER_TMO:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PORT_RECOVERY_TIMER_TMO\n"));
+ pm80xx_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_PORT_RECOVERY_TIMER_TMO,
+ 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);
+ break;
+ case HW_EVENT_PORT_RECOVER:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ break;
+ case EVENT_BROADCAST_ASYNCH_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("EVENT_BROADCAST_ASYNCH_EVENT\n"));
+ break;
+ default:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("Unknown event type 0x%x\n", eventType));
+ 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);
+ struct pm8001_phy *phy = &pm8001_ha->phy[phyid];
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("phy:0x%x status:0x%x\n",
+ phyid, status));
+ if (status == 0)
+ phy->phy_state = 0;
+ 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);
+
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
+ "SET CONTROLLER RESP: status 0x%x qlfr_pgcd 0x%x\n",
+ status, err_qlfr_pgcd));
+
+ 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_MSG_DBG(pm8001_ha,
+ pm8001_printk(" 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_MSG_DBG(pm8001_ha,
+ pm8001_printk(" 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_MSG_DBG(pm8001_ha,
+ pm8001_printk(" 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)
+{
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" pm80xx_addition_functionality\n"));
+
+ return 0;
+}
+
+/**
+ * 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_MSG_DBG(pm8001_ha, pm8001_printk(
+ "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_MSG_DBG(pm8001_ha,
+ pm8001_printk(" 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_MSG_DBG(pm8001_ha,
+ pm8001_printk(" pm80xx_addition_functionality\n"));
+
+ return 0;
+}
+
+/**
+ * process_one_iomb - process one outbound Queue memory block
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ __le32 pHeader = *(__le32 *)piomb;
+ u32 opc = (u32)((le32_to_cpu(pHeader)) & 0xFFF);
+
+ switch (opc) {
+ case OPC_OUB_ECHO:
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("OPC_OUB_ECHO\n"));
+ break;
+ case OPC_OUB_HW_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_HW_EVENT\n"));
+ mpi_hw_event(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_THERM_HW_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_THERMAL_EVENT\n"));
+ mpi_thermal_hw_event(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SSP_COMP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ 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"));
+ 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"));
+ pm8001_mpi_reg_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_DEREG_DEV:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("unresgister the deviece\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"));
+ break;
+ case OPC_OUB_SATA_COMP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ mpi_sata_event(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SSP_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ /*This is for target*/
+ break;
+ case OPC_OUB_SSP_RECV_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SSP_RECV_EVENT\n"));
+ /*This is for target*/
+ break;
+ case OPC_OUB_FW_FLASH_UPDATE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ break;
+ case OPC_OUB_GPIO_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_GPIO_EVENT\n"));
+ break;
+ case OPC_OUB_GENERAL_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ 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"));
+ 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"));
+ break;
+ case OPC_OUB_SAS_DIAG_EXECUTE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ break;
+ case OPC_OUB_SAS_HW_EVENT_ACK:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ break;
+ case OPC_OUB_SMP_ABORT_RSP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ 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"));
+ 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"));
+ break;
+ case OPC_OUB_SET_DEVICE_STATE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("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"));
+ break;
+ case OPC_OUB_SET_DEV_INFO:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SET_DEV_INFO\n"));
+ break;
+ /* spcv specifc commands */
+ case OPC_OUB_PHY_START_RESP:
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
+ "OPC_OUB_PHY_START_RESP opcode:%x\n", opc));
+ mpi_phy_start_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_PHY_STOP_RESP:
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
+ "OPC_OUB_PHY_STOP_RESP opcode:%x\n", opc));
+ mpi_phy_stop_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SET_CONTROLLER_CONFIG:
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
+ "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_MSG_DBG(pm8001_ha, pm8001_printk(
+ "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_MSG_DBG(pm8001_ha, pm8001_printk(
+ "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_MSG_DBG(pm8001_ha, pm8001_printk(
+ "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_MSG_DBG(pm8001_ha, pm8001_printk(
+ "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_MSG_DBG(pm8001_ha, pm8001_printk(
+ "OPC_OUB_KEK_MANAGEMENT_RESP opcode:%x\n", opc));
+ mpi_kek_management_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_DEK_MANAGEMENT_RESP:
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
+ "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_MSG_DBG(pm8001_ha, pm8001_printk(
+ "OPC_OUB_SSP_COALESCED_COMP_RESP opcode:%x\n", opc));
+ ssp_coalesced_comp_resp(pm8001_ha, piomb);
+ break;
+ default:
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
+ "Unknown outbound Queue IOMB OPC = 0x%x\n", opc));
+ break;
+ }
+}
+
+static int process_oq(struct pm8001_hba_info *pm8001_ha, u8 vec)
+{
+ struct outbound_queue_table *circularQ;
+ void *pMsg1 = NULL;
+ u8 uninitialized_var(bc);
+ u32 ret = MPI_IO_STATUS_FAIL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ circularQ = &pm8001_ha->outbnd_q_tbl[vec];
+ do {
+ 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 */
+ 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(&pm8001_ha->lock, flags);
+ return ret;
+}
+
+/* PCI_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 */
+};
+
+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));
+ }
+}
+
+/**
+ * pm8001_chip_smp_req - send a 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;
+ u32 req_len, resp_len;
+ struct smp_req smp_cmd;
+ u32 opc;
+ struct inbound_queue_table *circularQ;
+ char *preq_dma_addr = NULL;
+ __le64 tmp_addr;
+ u32 i, length;
+
+ 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);
+ 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);
+ 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;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ smp_cmd.tag = cpu_to_le32(ccb->ccb_tag);
+
+ length = sg_req->length;
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("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;
+
+ /* DIRECT MODE support only in spcv/ve */
+ pm8001_ha->smp_exp_mode = SMP_DIRECT;
+
+ tmp_addr = cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_req));
+ preq_dma_addr = (char *)phys_to_virt(tmp_addr);
+
+ /* INDIRECT MODE command settings. Use DMA */
+ if (pm8001_ha->smp_exp_mode == SMP_INDIRECT) {
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("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] = *(preq_dma_addr + 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_IO_DBG(pm8001_ha,
+ pm8001_printk("SMP REQUEST DIRECT MODE\n"));
+ for (i = 0; i < length; i++)
+ if (i < 16) {
+ smp_cmd.smp_req16[i] = *(preq_dma_addr+i);
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk(
+ "Byte[%d]:%x (DMA data:%x)\n",
+ i, smp_cmd.smp_req16[i],
+ *(preq_dma_addr)));
+ } else {
+ smp_cmd.smp_req[i] = *(preq_dma_addr+i);
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk(
+ "Byte[%d]:%x (DMA data:%x)\n",
+ i, smp_cmd.smp_req[i],
+ *(preq_dma_addr)));
+ }
+ }
+
+ build_smp_cmd(pm8001_dev->device_id, smp_cmd.tag,
+ &smp_cmd, pm8001_ha->smp_exp_mode, length);
+ pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&smp_cmd, 0);
+ return 0;
+
+err_out_2:
+ dma_unmap_sg(pm8001_ha->dev, &ccb->task->smp_task.smp_resp, 1,
+ PCI_DMA_FROMDEVICE);
+err_out:
+ dma_unmap_sg(pm8001_ha->dev, &ccb->task->smp_task.smp_req, 1,
+ PCI_DMA_TODEVICE);
+ return rc;
+}
+
+static int check_enc_sas_cmd(struct sas_task *task)
+{
+ if ((task->ssp_task.cdb[0] == READ_10)
+ || (task->ssp_task.cdb[0] == WRITE_10)
+ || (task->ssp_task.cdb[0] == 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;
+}
+
+/**
+ * pm80xx_chip_ssp_io_req - send a 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;
+ int ret;
+ u64 phys_addr;
+ struct inbound_queue_table *circularQ;
+ static u32 inb;
+ static u32 outb;
+ 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);
+ 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];
+
+ /* Check if encryption is set */
+ if (pm8001_ha->chip->encrypt &&
+ !(pm8001_ha->encrypt_info.status) && check_enc_sas_cmd(task)) {
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk(
+ "Encryption enabled.Sending Encrypt SAS command 0x%x\n",
+ task->ssp_task.cdb[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 +
+ offsetof(struct pm8001_ccb_info, buf_prd[0]);
+ 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;
+ } 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 = 0x6 << 4;
+ /* set tweak values. Should be the start lba */
+ ssp_cmd.twk_val0 = cpu_to_le32((task->ssp_task.cdb[2] << 24) |
+ (task->ssp_task.cdb[3] << 16) |
+ (task->ssp_task.cdb[4] << 8) |
+ (task->ssp_task.cdb[5]));
+ } else {
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk(
+ "Sending Normal SAS command 0x%x inb q %x\n",
+ task->ssp_task.cdb[0], inb));
+ /* 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 +
+ offsetof(struct pm8001_ccb_info, buf_prd[0]);
+ 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;
+ } 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;
+ }
+ }
+ ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &ssp_cmd, outb++);
+
+ /* rotate the outb queue */
+ outb = outb%PM8001_MAX_SPCV_OUTB_NUM;
+
+ return ret;
+}
+
+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;
+ u32 tag = ccb->ccb_tag;
+ int ret;
+ static u32 inb;
+ static u32 outb;
+ struct sata_start_req sata_cmd;
+ u32 hdr_tag, ncg_tag = 0;
+ u64 phys_addr;
+ u32 ATAP = 0x0;
+ u32 dir;
+ struct inbound_queue_table *circularQ;
+ unsigned long flags;
+ 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) {
+ ATAP = 0x04; /* no data*/
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("no data\n"));
+ } else if (likely(!task->ata_task.device_control_reg_update)) {
+ if (task->ata_task.dma_xfer) {
+ ATAP = 0x06; /* DMA */
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("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"));
+ }
+ }
+ 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.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_IO_DBG(pm8001_ha, pm8001_printk(
+ "Encryption enabled.Sending Encrypt SATA cmd 0x%x\n",
+ sata_cmd.sata_fis.command));
+ opc = OPC_INB_SATA_DIF_ENC_IO;
+
+ /* set encryption bit */
+ sata_cmd.ncqtag_atap_dir_m_dad =
+ cpu_to_le32(((ncg_tag & 0xff)<<16)|
+ ((ATAP & 0x3f) << 10) | 0x20 | dir);
+ /* dad (bit 0-1) is 0 */
+ /* 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 +
+ offsetof(struct pm8001_ccb_info, buf_prd[0]);
+ sata_cmd.enc_addr_low = lower_32_bits(phys_addr);
+ sata_cmd.enc_addr_high = 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 = lower_32_bits(dma_addr);
+ sata_cmd.enc_addr_high = upper_32_bits(dma_addr);
+ sata_cmd.enc_len = cpu_to_le32(task->total_xfer_len);
+ sata_cmd.enc_esgl = 0;
+ } 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 = 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_IO_DBG(pm8001_ha, pm8001_printk(
+ "Sending Normal SATA command 0x%x inb %x\n",
+ sata_cmd.sata_fis.command, inb));
+ /* dad (bit 0-1) is 0 */
+ sata_cmd.ncqtag_atap_dir_m_dad =
+ cpu_to_le32(((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 +
+ offsetof(struct pm8001_ccb_info, buf_prd[0]);
+ 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) {
+ 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;
+ } 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)));
+ }
+
+ /* Check for read log for failed drive and return */
+ if (sata_cmd.sata_fis.command == 0x2f) {
+ if (pm8001_ha_dev && ((pm8001_ha_dev->id & NCQ_READ_LOG_FLAG) ||
+ (pm8001_ha_dev->id & NCQ_ABORT_ALL_FLAG) ||
+ (pm8001_ha_dev->id & NCQ_2ND_RLE_FLAG))) {
+ struct task_status_struct *ts;
+
+ pm8001_ha_dev->id &= 0xDFFFFFFF;
+ ts = &task->task_status;
+
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_STAT_GOOD;
+ 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))) {
+ spin_unlock_irqrestore(&task->task_state_lock,
+ flags);
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("task 0x%p resp 0x%x "
+ " stat 0x%x but aborted by upper layer "
+ "\n", task, ts->resp, ts->stat));
+ pm8001_ccb_task_free(pm8001_ha, task, ccb, tag);
+ return 0;
+ } else if (task->uldd_task) {
+ spin_unlock_irqrestore(&task->task_state_lock,
+ flags);
+ pm8001_ccb_task_free(pm8001_ha, task, ccb, tag);
+ mb();/* ditto */
+ spin_unlock_irq(&pm8001_ha->lock);
+ task->task_done(task);
+ spin_lock_irq(&pm8001_ha->lock);
+ return 0;
+ } else if (!task->uldd_task) {
+ spin_unlock_irqrestore(&task->task_state_lock,
+ flags);
+ pm8001_ccb_task_free(pm8001_ha, task, ccb, tag);
+ mb();/*ditto*/
+ spin_unlock_irq(&pm8001_ha->lock);
+ task->task_done(task);
+ spin_lock_irq(&pm8001_ha->lock);
+ return 0;
+ }
+ }
+ }
+
+ ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc,
+ &sata_cmd, outb++);
+
+ /* rotate the outb queue */
+ outb = outb%PM8001_MAX_SPCV_OUTB_NUM;
+ return ret;
+}
+
+/**
+ * pm80xx_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
+pm80xx_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);
+
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("PHY START REQ for phy_id %d\n", phy_id));
+ /*
+ ** [0:7] PHY Identifier
+ ** [8:11] link rate 1.5G, 3G, 6G
+ ** [12:13] link mode 01b SAS mode; 10b SATA mode; 11b Auto mode
+ ** [14] 0b disable spin up hold; 1b enable spin up hold
+ ** [15] ob no change in current PHY analig setup 1b enable using SPAST
+ */
+ payload.ase_sh_lm_slr_phyid = cpu_to_le32(SPINHOLD_DISABLE |
+ LINKMODE_AUTO | LINKRATE_15 |
+ LINKRATE_30 | LINKRATE_60 | 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->sas_addr, SAS_ADDR_SIZE);
+ payload.sas_identify.phy_id = phy_id;
+ ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opcode, &payload, 0);
+ return ret;
+}
+
+/**
+ * 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 pm80xx_chip_phy_stop_req(struct pm8001_hba_info *pm8001_ha,
+ 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 = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opcode, &payload, 0);
+ 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;
+ struct inbound_queue_table *circularQ;
+ u32 linkrate, phy_id;
+ int rc, tag = 0xdeadbeef;
+ 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];
+
+ 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);
+
+ 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 ||
+ pm8001_dev->dev_type == SAS_EDGE_EXPANDER_DEVICE ||
+ pm8001_dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE)
+ 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;
+
+ 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) & 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);
+
+ rc = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
+
+ return rc;
+}
+
+/**
+ * pm80xx_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:
+ */
+static int pm80xx_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 = cpu_to_le32(1);
+ payload.phyop_phyid =
+ cpu_to_le32(((phy_op & 0xFF) << 8) | (phyId & 0xFF));
+ ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
+ return ret;
+}
+
+static u32 pm80xx_chip_is_our_interupt(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 value;
+#ifdef PM8001_USE_MSIX
+ return 1;
+#endif
+ 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.
+ */
+static irqreturn_t
+pm80xx_chip_isr(struct pm8001_hba_info *pm8001_ha, u8 vec)
+{
+ pm80xx_chip_interrupt_disable(pm8001_ha, vec);
+ process_oq(pm8001_ha, vec);
+ pm80xx_chip_interrupt_enable(pm8001_ha, vec);
+ return IRQ_HANDLED;
+}
+
+const struct pm8001_dispatch pm8001_80xx_dispatch = {
+ .name = "pmc80xx",
+ .chip_init = pm80xx_chip_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_interupt = pm80xx_chip_is_our_interupt,
+ .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,
+};
diff --git a/drivers/scsi/pm8001/pm80xx_hwi.h b/drivers/scsi/pm8001/pm80xx_hwi.h
new file mode 100644
index 000000000000..2b760ba75d7b
--- /dev/null
+++ b/drivers/scsi/pm8001/pm80xx_hwi.h
@@ -0,0 +1,1523 @@
+/*
+ * 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 (0x06 << 8)
+
+/* Thermal related */
+#define THERMAL_ENABLE 0x1
+#define THERMAL_LOG_ENABLE 0x1
+#define THERMAL_OP_CODE 0x6
+#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
+
+/*
+ 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)
+
+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 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;
+ 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)));
+
+/* 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)
+ */
+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 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;
+ u32 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
+/* 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_XFR_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 0x01
+#define SPCv_MSGU_CFG_TABLE_RESET 0x02
+#define SPCv_MSGU_CFG_TABLE_FREEZE 0x04
+#define SPCv_MSGU_CFG_TABLE_UNFREEZE 0x08
+#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_HOST_SCRATCH_PAD_6 0x6C
+#define MSGU_HOST_SCRATCH_PAD_7 0x70
+
+/* 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
+
+/* 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 */
+
+/* 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 */
+
+/* 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
+
+#endif
diff --git a/drivers/scsi/qla2xxx/Kconfig b/drivers/scsi/qla2xxx/Kconfig
index 317a7fdc3b82..23d607218ae8 100644
--- a/drivers/scsi/qla2xxx/Kconfig
+++ b/drivers/scsi/qla2xxx/Kconfig
@@ -24,7 +24,9 @@ config SCSI_QLA_FC
Firmware images can be retrieved from:
- ftp://ftp.qlogic.com/outgoing/linux/firmware/
+ http://ldriver.qlogic.com/firmware/
+
+ They are also included in the linux-firmware tree as well.
config TCM_QLA2XXX
tristate "TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs"
diff --git a/drivers/scsi/qla2xxx/qla_mr.c b/drivers/scsi/qla2xxx/qla_mr.c
index 729b74389f83..937fed8cb038 100644
--- a/drivers/scsi/qla2xxx/qla_mr.c
+++ b/drivers/scsi/qla2xxx/qla_mr.c
@@ -3003,12 +3003,10 @@ qlafx00_build_scsi_iocbs(srb_t *sp, struct cmd_type_7_fx00 *cmd_pkt,
/* Set transfer direction */
if (cmd->sc_data_direction == DMA_TO_DEVICE) {
- lcmd_pkt->cntrl_flags =
- __constant_cpu_to_le16(TMF_WRITE_DATA);
+ lcmd_pkt->cntrl_flags = TMF_WRITE_DATA;
vha->qla_stats.output_bytes += scsi_bufflen(cmd);
} else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
- lcmd_pkt->cntrl_flags =
- __constant_cpu_to_le16(TMF_READ_DATA);
+ lcmd_pkt->cntrl_flags = TMF_READ_DATA;
vha->qla_stats.input_bytes += scsi_bufflen(cmd);
}
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 5307bf86d5e0..ad72c1d85111 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -644,7 +644,7 @@ qla2x00_sp_free_dma(void *vha, void *ptr)
qla2x00_rel_sp(sp->fcport->vha, sp);
}
-void
+static void
qla2x00_sp_compl(void *data, void *ptr, int res)
{
struct qla_hw_data *ha = (struct qla_hw_data *)data;
diff --git a/drivers/scsi/qla4xxx/ql4_iocb.c b/drivers/scsi/qla4xxx/ql4_iocb.c
index 14fec976f634..fad71ed067ec 100644
--- a/drivers/scsi/qla4xxx/ql4_iocb.c
+++ b/drivers/scsi/qla4xxx/ql4_iocb.c
@@ -507,6 +507,7 @@ static int qla4xxx_send_mbox_iocb(struct scsi_qla_host *ha, struct mrb *mrb,
mrb->mbox_cmd = in_mbox[0];
wmb();
+ ha->iocb_cnt += mrb->iocb_cnt;
ha->isp_ops->queue_iocb(ha);
exit_mbox_iocb:
spin_unlock_irqrestore(&ha->hardware_lock, flags);
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index a47f99957ba8..4d231c12463e 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -2216,14 +2216,14 @@ static int qla4xxx_copy_to_fwddb_param(struct iscsi_bus_flash_session *sess,
fw_ddb_entry->iscsi_def_time2retain = cpu_to_le16(sess->time2retain);
fw_ddb_entry->tgt_portal_grp = cpu_to_le16(sess->tpgt);
fw_ddb_entry->mss = cpu_to_le16(conn->max_segment_size);
- fw_ddb_entry->tcp_xmt_wsf = cpu_to_le16(conn->tcp_xmit_wsf);
- fw_ddb_entry->tcp_rcv_wsf = cpu_to_le16(conn->tcp_recv_wsf);
+ fw_ddb_entry->tcp_xmt_wsf = (uint8_t) cpu_to_le32(conn->tcp_xmit_wsf);
+ fw_ddb_entry->tcp_rcv_wsf = (uint8_t) cpu_to_le32(conn->tcp_recv_wsf);
fw_ddb_entry->ipv4_tos = conn->ipv4_tos;
fw_ddb_entry->ipv6_flow_lbl = cpu_to_le16(conn->ipv6_flow_label);
fw_ddb_entry->ka_timeout = cpu_to_le16(conn->keepalive_timeout);
fw_ddb_entry->lcl_port = cpu_to_le16(conn->local_port);
- fw_ddb_entry->stat_sn = cpu_to_le16(conn->statsn);
- fw_ddb_entry->exp_stat_sn = cpu_to_le16(conn->exp_statsn);
+ fw_ddb_entry->stat_sn = cpu_to_le32(conn->statsn);
+ fw_ddb_entry->exp_stat_sn = cpu_to_le32(conn->exp_statsn);
fw_ddb_entry->ddb_link = cpu_to_le16(sess->discovery_parent_type);
fw_ddb_entry->chap_tbl_idx = cpu_to_le16(sess->chap_out_idx);
fw_ddb_entry->tsid = cpu_to_le16(sess->tsid);
@@ -5504,9 +5504,9 @@ static int qla4xxx_sysfs_ddb_is_non_persistent(struct device *dev, void *data)
* If this is invoked as a result of a userspace call then the entry is marked
* as nonpersistent using flash_state field.
**/
-int qla4xxx_sysfs_ddb_tgt_create(struct scsi_qla_host *ha,
- struct dev_db_entry *fw_ddb_entry,
- uint16_t *idx, int user)
+static int qla4xxx_sysfs_ddb_tgt_create(struct scsi_qla_host *ha,
+ struct dev_db_entry *fw_ddb_entry,
+ uint16_t *idx, int user)
{
struct iscsi_bus_flash_session *fnode_sess = NULL;
struct iscsi_bus_flash_conn *fnode_conn = NULL;
@@ -5605,6 +5605,7 @@ static int qla4xxx_sysfs_ddb_add(struct Scsi_Host *shost, const char *buf,
ql4_printk(KERN_ERR, ha,
"%s: A non-persistent entry %s found\n",
__func__, dev->kobj.name);
+ put_device(dev);
goto exit_ddb_add;
}
@@ -6112,8 +6113,7 @@ qla4xxx_sysfs_ddb_get_param(struct iscsi_bus_flash_session *fnode_sess,
int parent_type, parent_index = 0xffff;
int rc = 0;
- dev = iscsi_find_flashnode_conn(fnode_sess, NULL,
- iscsi_is_flashnode_conn_dev);
+ dev = iscsi_find_flashnode_conn(fnode_sess);
if (!dev)
return -EIO;
@@ -6276,8 +6276,7 @@ qla4xxx_sysfs_ddb_get_param(struct iscsi_bus_flash_session *fnode_sess,
rc = sprintf(buf, "\n");
break;
case ISCSI_FLASHNODE_DISCOVERY_PARENT_IDX:
- if ((fnode_sess->discovery_parent_idx) >= 0 &&
- (fnode_sess->discovery_parent_idx < MAX_DDB_ENTRIES))
+ if (fnode_sess->discovery_parent_idx < MAX_DDB_ENTRIES)
parent_index = fnode_sess->discovery_parent_idx;
rc = sprintf(buf, "%u\n", parent_index);
@@ -6287,8 +6286,7 @@ qla4xxx_sysfs_ddb_get_param(struct iscsi_bus_flash_session *fnode_sess,
parent_type = ISCSI_DISC_PARENT_ISNS;
else if (fnode_sess->discovery_parent_type == DDB_NO_LINK)
parent_type = ISCSI_DISC_PARENT_UNKNOWN;
- else if (fnode_sess->discovery_parent_type >= 0 &&
- fnode_sess->discovery_parent_type < MAX_DDB_ENTRIES)
+ else if (fnode_sess->discovery_parent_type < MAX_DDB_ENTRIES)
parent_type = ISCSI_DISC_PARENT_SENDTGT;
else
parent_type = ISCSI_DISC_PARENT_UNKNOWN;
@@ -6349,6 +6347,8 @@ qla4xxx_sysfs_ddb_get_param(struct iscsi_bus_flash_session *fnode_sess,
rc = -ENOSYS;
break;
}
+
+ put_device(dev);
return rc;
}
@@ -6368,20 +6368,11 @@ qla4xxx_sysfs_ddb_set_param(struct iscsi_bus_flash_session *fnode_sess,
{
struct Scsi_Host *shost = iscsi_flash_session_to_shost(fnode_sess);
struct scsi_qla_host *ha = to_qla_host(shost);
- struct dev_db_entry *fw_ddb_entry = NULL;
struct iscsi_flashnode_param_info *fnode_param;
struct nlattr *attr;
int rc = QLA_ERROR;
uint32_t rem = len;
- fw_ddb_entry = kzalloc(sizeof(*fw_ddb_entry), GFP_KERNEL);
- if (!fw_ddb_entry) {
- DEBUG2(ql4_printk(KERN_ERR, ha,
- "%s: Unable to allocate ddb buffer\n",
- __func__));
- return -ENOMEM;
- }
-
nla_for_each_attr(attr, data, len, rem) {
fnode_param = nla_data(attr);
@@ -6590,16 +6581,11 @@ static int qla4xxx_sysfs_ddb_delete(struct iscsi_bus_flash_session *fnode_sess)
struct dev_db_entry *fw_ddb_entry = NULL;
dma_addr_t fw_ddb_entry_dma;
uint16_t *ddb_cookie = NULL;
- size_t ddb_size;
+ size_t ddb_size = 0;
void *pddb = NULL;
int target_id;
int rc = 0;
- if (!fnode_sess) {
- rc = -EINVAL;
- goto exit_ddb_del;
- }
-
if (fnode_sess->is_boot_target) {
rc = -EPERM;
DEBUG2(ql4_printk(KERN_ERR, ha,
@@ -6631,8 +6617,7 @@ static int qla4xxx_sysfs_ddb_delete(struct iscsi_bus_flash_session *fnode_sess)
dev_db_start_offset += (fnode_sess->target_id *
sizeof(*fw_ddb_entry));
- dev_db_start_offset += (void *)&(fw_ddb_entry->cookie) -
- (void *)fw_ddb_entry;
+ dev_db_start_offset += offsetof(struct dev_db_entry, cookie);
ddb_size = sizeof(*ddb_cookie);
}
diff --git a/drivers/scsi/qla4xxx/ql4_version.h b/drivers/scsi/qla4xxx/ql4_version.h
index 83e0fec35d56..fe873cf7570d 100644
--- a/drivers/scsi/qla4xxx/ql4_version.h
+++ b/drivers/scsi/qla4xxx/ql4_version.h
@@ -5,4 +5,4 @@
* See LICENSE.qla4xxx for copyright and licensing details.
*/
-#define QLA4XXX_DRIVER_VERSION "5.03.00-k8"
+#define QLA4XXX_DRIVER_VERSION "5.03.00-k9"
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 5add6f4e7928..0a537a0515ca 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -1997,24 +1997,39 @@ out:
return ret;
}
-static unsigned int map_state(sector_t lba, unsigned int *num)
+static unsigned long lba_to_map_index(sector_t lba)
+{
+ if (scsi_debug_unmap_alignment) {
+ lba += scsi_debug_unmap_granularity -
+ scsi_debug_unmap_alignment;
+ }
+ do_div(lba, scsi_debug_unmap_granularity);
+
+ return lba;
+}
+
+static sector_t map_index_to_lba(unsigned long index)
{
- unsigned int granularity, alignment, mapped;
- sector_t block, next, end;
+ return index * scsi_debug_unmap_granularity -
+ scsi_debug_unmap_alignment;
+}
- granularity = scsi_debug_unmap_granularity;
- alignment = granularity - scsi_debug_unmap_alignment;
- block = lba + alignment;
- do_div(block, granularity);
+static unsigned int map_state(sector_t lba, unsigned int *num)
+{
+ sector_t end;
+ unsigned int mapped;
+ unsigned long index;
+ unsigned long next;
- mapped = test_bit(block, map_storep);
+ index = lba_to_map_index(lba);
+ mapped = test_bit(index, map_storep);
if (mapped)
- next = find_next_zero_bit(map_storep, map_size, block);
+ next = find_next_zero_bit(map_storep, map_size, index);
else
- next = find_next_bit(map_storep, map_size, block);
+ next = find_next_bit(map_storep, map_size, index);
- end = next * granularity - scsi_debug_unmap_alignment;
+ end = min_t(sector_t, sdebug_store_sectors, map_index_to_lba(next));
*num = end - lba;
return mapped;
@@ -2022,47 +2037,37 @@ static unsigned int map_state(sector_t lba, unsigned int *num)
static void map_region(sector_t lba, unsigned int len)
{
- unsigned int granularity, alignment;
sector_t end = lba + len;
- granularity = scsi_debug_unmap_granularity;
- alignment = granularity - scsi_debug_unmap_alignment;
-
while (lba < end) {
- sector_t block, rem;
-
- block = lba + alignment;
- rem = do_div(block, granularity);
+ unsigned long index = lba_to_map_index(lba);
- if (block < map_size)
- set_bit(block, map_storep);
+ if (index < map_size)
+ set_bit(index, map_storep);
- lba += granularity - rem;
+ lba = map_index_to_lba(index + 1);
}
}
static void unmap_region(sector_t lba, unsigned int len)
{
- unsigned int granularity, alignment;
sector_t end = lba + len;
- granularity = scsi_debug_unmap_granularity;
- alignment = granularity - scsi_debug_unmap_alignment;
-
while (lba < end) {
- sector_t block, rem;
-
- block = lba + alignment;
- rem = do_div(block, granularity);
+ unsigned long index = lba_to_map_index(lba);
- if (rem == 0 && lba + granularity < end && block < map_size) {
- clear_bit(block, map_storep);
- if (scsi_debug_lbprz)
+ if (lba == map_index_to_lba(index) &&
+ lba + scsi_debug_unmap_granularity <= end &&
+ index < map_size) {
+ clear_bit(index, map_storep);
+ if (scsi_debug_lbprz) {
memset(fake_storep +
- block * scsi_debug_sector_size, 0,
- scsi_debug_sector_size);
+ lba * scsi_debug_sector_size, 0,
+ scsi_debug_sector_size *
+ scsi_debug_unmap_granularity);
+ }
}
- lba += granularity - rem;
+ lba = map_index_to_lba(index + 1);
}
}
@@ -2089,7 +2094,7 @@ static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba,
write_lock_irqsave(&atomic_rw, iflags);
ret = do_device_access(SCpnt, devip, lba, num, 1);
- if (scsi_debug_unmap_granularity)
+ if (scsi_debug_lbp())
map_region(lba, num);
write_unlock_irqrestore(&atomic_rw, iflags);
if (-1 == ret)
@@ -2122,7 +2127,7 @@ static int resp_write_same(struct scsi_cmnd *scmd, unsigned long long lba,
write_lock_irqsave(&atomic_rw, iflags);
- if (unmap && scsi_debug_unmap_granularity) {
+ if (unmap && scsi_debug_lbp()) {
unmap_region(lba, num);
goto out;
}
@@ -2146,7 +2151,7 @@ static int resp_write_same(struct scsi_cmnd *scmd, unsigned long long lba,
fake_storep + (lba * scsi_debug_sector_size),
scsi_debug_sector_size);
- if (scsi_debug_unmap_granularity)
+ if (scsi_debug_lbp())
map_region(lba, num);
out:
write_unlock_irqrestore(&atomic_rw, iflags);
@@ -3389,8 +3394,6 @@ static int __init scsi_debug_init(void)
/* Logical Block Provisioning */
if (scsi_debug_lbp()) {
- unsigned int map_bytes;
-
scsi_debug_unmap_max_blocks =
clamp(scsi_debug_unmap_max_blocks, 0U, 0xffffffffU);
@@ -3401,16 +3404,16 @@ static int __init scsi_debug_init(void)
clamp(scsi_debug_unmap_granularity, 1U, 0xffffffffU);
if (scsi_debug_unmap_alignment &&
- scsi_debug_unmap_granularity < scsi_debug_unmap_alignment) {
+ scsi_debug_unmap_granularity <=
+ scsi_debug_unmap_alignment) {
printk(KERN_ERR
- "%s: ERR: unmap_granularity < unmap_alignment\n",
+ "%s: ERR: unmap_granularity <= unmap_alignment\n",
__func__);
return -EINVAL;
}
- map_size = (sdebug_store_sectors / scsi_debug_unmap_granularity);
- map_bytes = map_size >> 3;
- map_storep = vmalloc(map_bytes);
+ map_size = lba_to_map_index(sdebug_store_sectors - 1) + 1;
+ map_storep = vmalloc(BITS_TO_LONGS(map_size) * sizeof(long));
printk(KERN_INFO "scsi_debug_init: %lu provisioning blocks\n",
map_size);
@@ -3421,7 +3424,7 @@ static int __init scsi_debug_init(void)
goto free_vm;
}
- memset(map_storep, 0x0, map_bytes);
+ bitmap_zero(map_storep, map_size);
/* Map first 1KB for partition table */
if (scsi_debug_num_parts)
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index c1b05a83d403..f43de1e56420 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -25,6 +25,7 @@
#include <linux/interrupt.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
+#include <linux/jiffies.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -791,32 +792,48 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
struct scsi_device *sdev = scmd->device;
struct Scsi_Host *shost = sdev->host;
DECLARE_COMPLETION_ONSTACK(done);
- unsigned long timeleft;
+ unsigned long timeleft = timeout;
struct scsi_eh_save ses;
+ const unsigned long stall_for = msecs_to_jiffies(100);
int rtn;
+retry:
scsi_eh_prep_cmnd(scmd, &ses, cmnd, cmnd_size, sense_bytes);
shost->eh_action = &done;
scsi_log_send(scmd);
scmd->scsi_done = scsi_eh_done;
- shost->hostt->queuecommand(shost, scmd);
-
- timeleft = wait_for_completion_timeout(&done, timeout);
+ rtn = shost->hostt->queuecommand(shost, scmd);
+ if (rtn) {
+ if (timeleft > stall_for) {
+ scsi_eh_restore_cmnd(scmd, &ses);
+ timeleft -= stall_for;
+ msleep(jiffies_to_msecs(stall_for));
+ goto retry;
+ }
+ /* signal not to enter either branch of the if () below */
+ timeleft = 0;
+ rtn = NEEDS_RETRY;
+ } else {
+ timeleft = wait_for_completion_timeout(&done, timeout);
+ }
shost->eh_action = NULL;
- scsi_log_completion(scmd, SUCCESS);
+ scsi_log_completion(scmd, rtn);
SCSI_LOG_ERROR_RECOVERY(3,
printk("%s: scmd: %p, timeleft: %ld\n",
__func__, scmd, timeleft));
/*
- * If there is time left scsi_eh_done got called, and we will
- * examine the actual status codes to see whether the command
- * actually did complete normally, else tell the host to forget
- * about this command.
+ * If there is time left scsi_eh_done got called, and we will examine
+ * the actual status codes to see whether the command actually did
+ * complete normally, else if we have a zero return and no time left,
+ * the command must still be pending, so abort it and return FAILED.
+ * If we never actually managed to issue the command, because
+ * ->queuecommand() kept returning non zero, use the rtn = FAILED
+ * value above (so don't execute either branch of the if)
*/
if (timeleft) {
rtn = scsi_eh_completed_normally(scmd);
@@ -837,7 +854,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
rtn = FAILED;
break;
}
- } else {
+ } else if (!rtn) {
scsi_abort_eh_cmnd(scmd);
rtn = FAILED;
}
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index c31187d79343..86d522004a20 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -276,11 +276,10 @@ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
}
EXPORT_SYMBOL(scsi_execute);
-
-int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
+int scsi_execute_req_flags(struct scsi_device *sdev, const unsigned char *cmd,
int data_direction, void *buffer, unsigned bufflen,
struct scsi_sense_hdr *sshdr, int timeout, int retries,
- int *resid)
+ int *resid, int flags)
{
char *sense = NULL;
int result;
@@ -291,14 +290,14 @@ int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
return DRIVER_ERROR << 24;
}
result = scsi_execute(sdev, cmd, data_direction, buffer, bufflen,
- sense, timeout, retries, 0, resid);
+ sense, timeout, retries, flags, resid);
if (sshdr)
scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, sshdr);
kfree(sense);
return result;
}
-EXPORT_SYMBOL(scsi_execute_req);
+EXPORT_SYMBOL(scsi_execute_req_flags);
/*
* Function: scsi_init_cmd_errh()
diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c
index 8f6b12cbd224..42539ee2cb11 100644
--- a/drivers/scsi/scsi_pm.c
+++ b/drivers/scsi/scsi_pm.c
@@ -144,33 +144,83 @@ static int scsi_bus_restore(struct device *dev)
#ifdef CONFIG_PM_RUNTIME
+static int sdev_blk_runtime_suspend(struct scsi_device *sdev,
+ int (*cb)(struct device *))
+{
+ int err;
+
+ err = blk_pre_runtime_suspend(sdev->request_queue);
+ if (err)
+ return err;
+ if (cb)
+ err = cb(&sdev->sdev_gendev);
+ blk_post_runtime_suspend(sdev->request_queue, err);
+
+ return err;
+}
+
+static int sdev_runtime_suspend(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ int (*cb)(struct device *) = pm ? pm->runtime_suspend : NULL;
+ struct scsi_device *sdev = to_scsi_device(dev);
+ int err;
+
+ if (sdev->request_queue->dev)
+ return sdev_blk_runtime_suspend(sdev, cb);
+
+ err = scsi_dev_type_suspend(dev, cb);
+ if (err == -EAGAIN)
+ pm_schedule_suspend(dev, jiffies_to_msecs(
+ round_jiffies_up_relative(HZ/10)));
+ return err;
+}
+
static int scsi_runtime_suspend(struct device *dev)
{
int err = 0;
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
dev_dbg(dev, "scsi_runtime_suspend\n");
- if (scsi_is_sdev_device(dev)) {
- err = scsi_dev_type_suspend(dev,
- pm ? pm->runtime_suspend : NULL);
- if (err == -EAGAIN)
- pm_schedule_suspend(dev, jiffies_to_msecs(
- round_jiffies_up_relative(HZ/10)));
- }
+ if (scsi_is_sdev_device(dev))
+ err = sdev_runtime_suspend(dev);
/* Insert hooks here for targets, hosts, and transport classes */
return err;
}
-static int scsi_runtime_resume(struct device *dev)
+static int sdev_blk_runtime_resume(struct scsi_device *sdev,
+ int (*cb)(struct device *))
{
int err = 0;
+
+ blk_pre_runtime_resume(sdev->request_queue);
+ if (cb)
+ err = cb(&sdev->sdev_gendev);
+ blk_post_runtime_resume(sdev->request_queue, err);
+
+ return err;
+}
+
+static int sdev_runtime_resume(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ int (*cb)(struct device *) = pm ? pm->runtime_resume : NULL;
+
+ if (sdev->request_queue->dev)
+ return sdev_blk_runtime_resume(sdev, cb);
+ else
+ return scsi_dev_type_resume(dev, cb);
+}
+
+static int scsi_runtime_resume(struct device *dev)
+{
+ int err = 0;
dev_dbg(dev, "scsi_runtime_resume\n");
if (scsi_is_sdev_device(dev))
- err = scsi_dev_type_resume(dev, pm ? pm->runtime_resume : NULL);
+ err = sdev_runtime_resume(dev);
/* Insert hooks here for targets, hosts, and transport classes */
@@ -185,10 +235,18 @@ static int scsi_runtime_idle(struct device *dev)
/* Insert hooks here for targets, hosts, and transport classes */
- if (scsi_is_sdev_device(dev))
- err = pm_schedule_suspend(dev, 100);
- else
+ if (scsi_is_sdev_device(dev)) {
+ struct scsi_device *sdev = to_scsi_device(dev);
+
+ if (sdev->request_queue->dev) {
+ pm_runtime_mark_last_busy(dev);
+ err = pm_runtime_autosuspend(dev);
+ } else {
+ err = pm_runtime_suspend(dev);
+ }
+ } else {
err = pm_runtime_suspend(dev);
+ }
return err;
}
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index 47799a33d6ca..133926b1bb78 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -1019,8 +1019,7 @@ exit_match_index:
/**
* iscsi_get_flashnode_by_index -finds flashnode session entry by index
* @shost: pointer to host data
- * @data: pointer to data containing value to use for comparison
- * @fn: function pointer that does actual comparison
+ * @idx: index to match
*
* Finds the flashnode session object for the passed index
*
@@ -1029,13 +1028,13 @@ exit_match_index:
* %NULL on failure
*/
static struct iscsi_bus_flash_session *
-iscsi_get_flashnode_by_index(struct Scsi_Host *shost, void *data,
- int (*fn)(struct device *dev, void *data))
+iscsi_get_flashnode_by_index(struct Scsi_Host *shost, uint32_t idx)
{
struct iscsi_bus_flash_session *fnode_sess = NULL;
struct device *dev;
- dev = device_find_child(&shost->shost_gendev, data, fn);
+ dev = device_find_child(&shost->shost_gendev, &idx,
+ flashnode_match_index);
if (dev)
fnode_sess = iscsi_dev_to_flash_session(dev);
@@ -1059,18 +1058,13 @@ struct device *
iscsi_find_flashnode_sess(struct Scsi_Host *shost, void *data,
int (*fn)(struct device *dev, void *data))
{
- struct device *dev;
-
- dev = device_find_child(&shost->shost_gendev, data, fn);
- return dev;
+ return device_find_child(&shost->shost_gendev, data, fn);
}
EXPORT_SYMBOL_GPL(iscsi_find_flashnode_sess);
/**
* iscsi_find_flashnode_conn - finds flashnode connection entry
* @fnode_sess: pointer to parent flashnode session entry
- * @data: pointer to data containing value to use for comparison
- * @fn: function pointer that does actual comparison
*
* Finds the flashnode connection object comparing the data passed using logic
* defined in passed function pointer
@@ -1080,14 +1074,10 @@ EXPORT_SYMBOL_GPL(iscsi_find_flashnode_sess);
* %NULL on failure
*/
struct device *
-iscsi_find_flashnode_conn(struct iscsi_bus_flash_session *fnode_sess,
- void *data,
- int (*fn)(struct device *dev, void *data))
+iscsi_find_flashnode_conn(struct iscsi_bus_flash_session *fnode_sess)
{
- struct device *dev;
-
- dev = device_find_child(&fnode_sess->dev, data, fn);
- return dev;
+ return device_find_child(&fnode_sess->dev, NULL,
+ iscsi_is_flashnode_conn_dev);
}
EXPORT_SYMBOL_GPL(iscsi_find_flashnode_conn);
@@ -2808,7 +2798,7 @@ static int iscsi_set_flashnode_param(struct iscsi_transport *transport,
struct iscsi_bus_flash_session *fnode_sess;
struct iscsi_bus_flash_conn *fnode_conn;
struct device *dev;
- uint32_t *idx;
+ uint32_t idx;
int err = 0;
if (!transport->set_flashnode_param) {
@@ -2824,25 +2814,27 @@ static int iscsi_set_flashnode_param(struct iscsi_transport *transport,
goto put_host;
}
- idx = &ev->u.set_flashnode.flashnode_idx;
- fnode_sess = iscsi_get_flashnode_by_index(shost, idx,
- flashnode_match_index);
+ idx = ev->u.set_flashnode.flashnode_idx;
+ fnode_sess = iscsi_get_flashnode_by_index(shost, idx);
if (!fnode_sess) {
pr_err("%s could not find flashnode %u for host no %u\n",
- __func__, *idx, ev->u.set_flashnode.host_no);
+ __func__, idx, ev->u.set_flashnode.host_no);
err = -ENODEV;
goto put_host;
}
- dev = iscsi_find_flashnode_conn(fnode_sess, NULL,
- iscsi_is_flashnode_conn_dev);
+ dev = iscsi_find_flashnode_conn(fnode_sess);
if (!dev) {
err = -ENODEV;
- goto put_host;
+ goto put_sess;
}
fnode_conn = iscsi_dev_to_flash_conn(dev);
err = transport->set_flashnode_param(fnode_sess, fnode_conn, data, len);
+ put_device(dev);
+
+put_sess:
+ put_device(&fnode_sess->dev);
put_host:
scsi_host_put(shost);
@@ -2891,7 +2883,7 @@ static int iscsi_del_flashnode(struct iscsi_transport *transport,
{
struct Scsi_Host *shost;
struct iscsi_bus_flash_session *fnode_sess;
- uint32_t *idx;
+ uint32_t idx;
int err = 0;
if (!transport->del_flashnode) {
@@ -2907,17 +2899,17 @@ static int iscsi_del_flashnode(struct iscsi_transport *transport,
goto put_host;
}
- idx = &ev->u.del_flashnode.flashnode_idx;
- fnode_sess = iscsi_get_flashnode_by_index(shost, idx,
- flashnode_match_index);
+ idx = ev->u.del_flashnode.flashnode_idx;
+ fnode_sess = iscsi_get_flashnode_by_index(shost, idx);
if (!fnode_sess) {
pr_err("%s could not find flashnode %u for host no %u\n",
- __func__, *idx, ev->u.del_flashnode.host_no);
+ __func__, idx, ev->u.del_flashnode.host_no);
err = -ENODEV;
goto put_host;
}
err = transport->del_flashnode(fnode_sess);
+ put_device(&fnode_sess->dev);
put_host:
scsi_host_put(shost);
@@ -2933,7 +2925,7 @@ static int iscsi_login_flashnode(struct iscsi_transport *transport,
struct iscsi_bus_flash_session *fnode_sess;
struct iscsi_bus_flash_conn *fnode_conn;
struct device *dev;
- uint32_t *idx;
+ uint32_t idx;
int err = 0;
if (!transport->login_flashnode) {
@@ -2949,25 +2941,27 @@ static int iscsi_login_flashnode(struct iscsi_transport *transport,
goto put_host;
}
- idx = &ev->u.login_flashnode.flashnode_idx;
- fnode_sess = iscsi_get_flashnode_by_index(shost, idx,
- flashnode_match_index);
+ idx = ev->u.login_flashnode.flashnode_idx;
+ fnode_sess = iscsi_get_flashnode_by_index(shost, idx);
if (!fnode_sess) {
pr_err("%s could not find flashnode %u for host no %u\n",
- __func__, *idx, ev->u.login_flashnode.host_no);
+ __func__, idx, ev->u.login_flashnode.host_no);
err = -ENODEV;
goto put_host;
}
- dev = iscsi_find_flashnode_conn(fnode_sess, NULL,
- iscsi_is_flashnode_conn_dev);
+ dev = iscsi_find_flashnode_conn(fnode_sess);
if (!dev) {
err = -ENODEV;
- goto put_host;
+ goto put_sess;
}
fnode_conn = iscsi_dev_to_flash_conn(dev);
err = transport->login_flashnode(fnode_sess, fnode_conn);
+ put_device(dev);
+
+put_sess:
+ put_device(&fnode_sess->dev);
put_host:
scsi_host_put(shost);
@@ -2983,7 +2977,7 @@ static int iscsi_logout_flashnode(struct iscsi_transport *transport,
struct iscsi_bus_flash_session *fnode_sess;
struct iscsi_bus_flash_conn *fnode_conn;
struct device *dev;
- uint32_t *idx;
+ uint32_t idx;
int err = 0;
if (!transport->logout_flashnode) {
@@ -2999,26 +2993,28 @@ static int iscsi_logout_flashnode(struct iscsi_transport *transport,
goto put_host;
}
- idx = &ev->u.logout_flashnode.flashnode_idx;
- fnode_sess = iscsi_get_flashnode_by_index(shost, idx,
- flashnode_match_index);
+ idx = ev->u.logout_flashnode.flashnode_idx;
+ fnode_sess = iscsi_get_flashnode_by_index(shost, idx);
if (!fnode_sess) {
pr_err("%s could not find flashnode %u for host no %u\n",
- __func__, *idx, ev->u.logout_flashnode.host_no);
+ __func__, idx, ev->u.logout_flashnode.host_no);
err = -ENODEV;
goto put_host;
}
- dev = iscsi_find_flashnode_conn(fnode_sess, NULL,
- iscsi_is_flashnode_conn_dev);
+ dev = iscsi_find_flashnode_conn(fnode_sess);
if (!dev) {
err = -ENODEV;
- goto put_host;
+ goto put_sess;
}
fnode_conn = iscsi_dev_to_flash_conn(dev);
err = transport->logout_flashnode(fnode_sess, fnode_conn);
+ put_device(dev);
+
+put_sess:
+ put_device(&fnode_sess->dev);
put_host:
scsi_host_put(shost);
@@ -3985,8 +3981,10 @@ static __init int iscsi_transport_init(void)
}
iscsi_eh_timer_workq = create_singlethread_workqueue("iscsi_eh");
- if (!iscsi_eh_timer_workq)
+ if (!iscsi_eh_timer_workq) {
+ err = -ENOMEM;
goto release_nls;
+ }
return 0;
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index e6689776b4f6..c1c555242d0d 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -142,6 +142,7 @@ sd_store_cache_type(struct device *dev, struct device_attribute *attr,
char *buffer_data;
struct scsi_mode_data data;
struct scsi_sense_hdr sshdr;
+ const char *temp = "temporary ";
int len;
if (sdp->type != TYPE_DISK)
@@ -150,6 +151,13 @@ sd_store_cache_type(struct device *dev, struct device_attribute *attr,
* it's not worth the risk */
return -EINVAL;
+ if (strncmp(buf, temp, sizeof(temp) - 1) == 0) {
+ buf += sizeof(temp) - 1;
+ sdkp->cache_override = 1;
+ } else {
+ sdkp->cache_override = 0;
+ }
+
for (i = 0; i < ARRAY_SIZE(sd_cache_types); i++) {
len = strlen(sd_cache_types[i]);
if (strncmp(sd_cache_types[i], buf, len) == 0 &&
@@ -162,6 +170,13 @@ sd_store_cache_type(struct device *dev, struct device_attribute *attr,
return -EINVAL;
rcd = ct & 0x01 ? 1 : 0;
wce = ct & 0x02 ? 1 : 0;
+
+ if (sdkp->cache_override) {
+ sdkp->WCE = wce;
+ sdkp->RCD = rcd;
+ return count;
+ }
+
if (scsi_mode_sense(sdp, 0x08, 8, buffer, sizeof(buffer), SD_TIMEOUT,
SD_MAX_RETRIES, &data, NULL))
return -EINVAL;
@@ -1121,10 +1136,6 @@ static int sd_open(struct block_device *bdev, fmode_t mode)
sdev = sdkp->device;
- retval = scsi_autopm_get_device(sdev);
- if (retval)
- goto error_autopm;
-
/*
* If the device is in error recovery, wait until it is done.
* If the device is offline, then disallow any access to it.
@@ -1169,8 +1180,6 @@ static int sd_open(struct block_device *bdev, fmode_t mode)
return 0;
error_out:
- scsi_autopm_put_device(sdev);
-error_autopm:
scsi_disk_put(sdkp);
return retval;
}
@@ -1205,7 +1214,6 @@ static void sd_release(struct gendisk *disk, fmode_t mode)
* XXX is followed by a "rmmod sd_mod"?
*/
- scsi_autopm_put_device(sdev);
scsi_disk_put(sdkp);
}
@@ -1366,14 +1374,9 @@ static unsigned int sd_check_events(struct gendisk *disk, unsigned int clearing)
retval = -ENODEV;
if (scsi_block_when_processing_errors(sdp)) {
- retval = scsi_autopm_get_device(sdp);
- if (retval)
- goto out;
-
sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES,
sshdr);
- scsi_autopm_put_device(sdp);
}
/* failed to execute TUR, assume media not present */
@@ -1423,8 +1426,9 @@ static int sd_sync_cache(struct scsi_disk *sdkp)
* Leave the rest of the command zero to indicate
* flush everything.
*/
- res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
- SD_FLUSH_TIMEOUT, SD_MAX_RETRIES, NULL);
+ res = scsi_execute_req_flags(sdp, cmd, DMA_NONE, NULL, 0,
+ &sshdr, SD_FLUSH_TIMEOUT,
+ SD_MAX_RETRIES, NULL, REQ_PM);
if (res == 0)
break;
}
@@ -2318,6 +2322,10 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
int old_rcd = sdkp->RCD;
int old_dpofua = sdkp->DPOFUA;
+
+ if (sdkp->cache_override)
+ return;
+
first_len = 4;
if (sdp->skip_ms_page_8) {
if (sdp->type == TYPE_RBC)
@@ -2811,6 +2819,7 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
sdkp->capacity = 0;
sdkp->media_present = 1;
sdkp->write_prot = 0;
+ sdkp->cache_override = 0;
sdkp->WCE = 0;
sdkp->RCD = 0;
sdkp->ATO = 0;
@@ -2837,6 +2846,7 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
sdp->removable ? "removable " : "");
+ blk_pm_runtime_init(sdp->request_queue, dev);
scsi_autopm_put_device(sdp);
put_device(&sdkp->dev);
}
@@ -3020,8 +3030,8 @@ static int sd_start_stop_device(struct scsi_disk *sdkp, int start)
if (!scsi_device_online(sdp))
return -ENODEV;
- res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
- SD_TIMEOUT, SD_MAX_RETRIES, NULL);
+ res = scsi_execute_req_flags(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
+ SD_TIMEOUT, SD_MAX_RETRIES, NULL, REQ_PM);
if (res) {
sd_printk(KERN_WARNING, sdkp, "START_STOP FAILED\n");
sd_print_result(sdkp, res);
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
index 74a1e4ca5401..2386aeb41fe8 100644
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -73,6 +73,7 @@ struct scsi_disk {
u8 protection_type;/* Data Integrity Field */
u8 provisioning_mode;
unsigned ATO : 1; /* state of disk ATO bit */
+ unsigned cache_override : 1; /* temp override of WCE,RCD */
unsigned WCE : 1; /* state of disk WCE bit */
unsigned RCD : 1; /* state of disk RCD bit, unused */
unsigned DPOFUA : 1; /* state of disk DPOFUA bit */
diff --git a/drivers/scsi/sd_dif.c b/drivers/scsi/sd_dif.c
index 04998f36e507..6174ca4ea275 100644
--- a/drivers/scsi/sd_dif.c
+++ b/drivers/scsi/sd_dif.c
@@ -93,14 +93,6 @@ static int sd_dif_type1_verify(struct blk_integrity_exchg *bix, csum_fn *fn)
if (sdt->app_tag == 0xffff)
return 0;
- /* Bad ref tag received from disk */
- if (sdt->ref_tag == 0xffffffff) {
- printk(KERN_ERR
- "%s: bad phys ref tag on sector %lu\n",
- bix->disk_name, (unsigned long)sector);
- return -EIO;
- }
-
if (be32_to_cpu(sdt->ref_tag) != (sector & 0xffffffff)) {
printk(KERN_ERR
"%s: ref tag error on sector %lu (rcvd %u)\n",
diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
index 0371047c5922..35faf24c6044 100644
--- a/drivers/scsi/ufs/Kconfig
+++ b/drivers/scsi/ufs/Kconfig
@@ -57,3 +57,14 @@ config SCSI_UFSHCD_PCI
If you have a controller with this interface, say Y or M here.
If unsure, say N.
+
+config SCSI_UFSHCD_PLATFORM
+ tristate "Platform bus based UFS Controller support"
+ depends on SCSI_UFSHCD
+ ---help---
+ This selects the UFS host controller support. Select this if
+ you have an UFS controller on Platform bus.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
index 9eda0dfbd6df..1e5bd48457d6 100644
--- a/drivers/scsi/ufs/Makefile
+++ b/drivers/scsi/ufs/Makefile
@@ -1,3 +1,4 @@
# UFSHCD makefile
obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
+obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
new file mode 100644
index 000000000000..03319acd9c72
--- /dev/null
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -0,0 +1,217 @@
+/*
+ * Universal Flash Storage Host controller Platform bus based glue driver
+ *
+ * This code is based on drivers/scsi/ufs/ufshcd-pltfrm.c
+ * Copyright (C) 2011-2013 Samsung India Software Operations
+ *
+ * Authors:
+ * Santosh Yaraganavi <santosh.sy@samsung.com>
+ * Vinayak Holikatti <h.vinayak@samsung.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.
+ * See the COPYING file in the top-level directory or visit
+ * <http://www.gnu.org/licenses/gpl-2.0.html>
+ *
+ * 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.
+ *
+ * This program is provided "AS IS" and "WITH ALL FAULTS" and
+ * without warranty of any kind. You are solely responsible for
+ * determining the appropriateness of using and distributing
+ * the program and assume all risks associated with your exercise
+ * of rights with respect to the program, including but not limited
+ * to infringement of third party rights, the risks and costs of
+ * program errors, damage to or loss of data, programs or equipment,
+ * and unavailability or interruption of operations. Under no
+ * circumstances will the contributor of this Program be liable for
+ * any damages of any kind arising from your use or distribution of
+ * this program.
+ */
+
+#include "ufshcd.h"
+#include <linux/platform_device.h>
+
+#ifdef CONFIG_PM
+/**
+ * ufshcd_pltfrm_suspend - suspend power management function
+ * @dev: pointer to device handle
+ *
+ *
+ * Returns 0
+ */
+static int ufshcd_pltfrm_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+
+ /*
+ * TODO:
+ * 1. Call ufshcd_suspend
+ * 2. Do bus specific power management
+ */
+
+ disable_irq(hba->irq);
+
+ return 0;
+}
+
+/**
+ * ufshcd_pltfrm_resume - resume power management function
+ * @dev: pointer to device handle
+ *
+ * Returns 0
+ */
+static int ufshcd_pltfrm_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+
+ /*
+ * TODO:
+ * 1. Call ufshcd_resume.
+ * 2. Do bus specific wake up
+ */
+
+ enable_irq(hba->irq);
+
+ return 0;
+}
+#else
+#define ufshcd_pltfrm_suspend NULL
+#define ufshcd_pltfrm_resume NULL
+#endif
+
+/**
+ * ufshcd_pltfrm_probe - probe routine of the driver
+ * @pdev: pointer to Platform device handle
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_pltfrm_probe(struct platform_device *pdev)
+{
+ struct ufs_hba *hba;
+ void __iomem *mmio_base;
+ struct resource *mem_res;
+ struct resource *irq_res;
+ resource_size_t mem_size;
+ int err;
+ struct device *dev = &pdev->dev;
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem_res) {
+ dev_err(&pdev->dev,
+ "Memory resource not available\n");
+ err = -ENODEV;
+ goto out_error;
+ }
+
+ mem_size = resource_size(mem_res);
+ if (!request_mem_region(mem_res->start, mem_size, "ufshcd")) {
+ dev_err(&pdev->dev,
+ "Cannot reserve the memory resource\n");
+ err = -EBUSY;
+ goto out_error;
+ }
+
+ mmio_base = ioremap_nocache(mem_res->start, mem_size);
+ if (!mmio_base) {
+ dev_err(&pdev->dev, "memory map failed\n");
+ err = -ENOMEM;
+ goto out_release_regions;
+ }
+
+ irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq_res) {
+ dev_err(&pdev->dev, "IRQ resource not available\n");
+ err = -ENODEV;
+ goto out_iounmap;
+ }
+
+ err = dma_set_coherent_mask(dev, dev->coherent_dma_mask);
+ if (err) {
+ dev_err(&pdev->dev, "set dma mask failed\n");
+ goto out_iounmap;
+ }
+
+ err = ufshcd_init(&pdev->dev, &hba, mmio_base, irq_res->start);
+ if (err) {
+ dev_err(&pdev->dev, "Intialization failed\n");
+ goto out_iounmap;
+ }
+
+ platform_set_drvdata(pdev, hba);
+
+ return 0;
+
+out_iounmap:
+ iounmap(mmio_base);
+out_release_regions:
+ release_mem_region(mem_res->start, mem_size);
+out_error:
+ return err;
+}
+
+/**
+ * ufshcd_pltfrm_remove - remove platform driver routine
+ * @pdev: pointer to platform device handle
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_pltfrm_remove(struct platform_device *pdev)
+{
+ struct resource *mem_res;
+ resource_size_t mem_size;
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+
+ disable_irq(hba->irq);
+
+ /* Some buggy controllers raise interrupt after
+ * the resources are removed. So first we unregister the
+ * irq handler and then the resources used by driver
+ */
+
+ free_irq(hba->irq, hba);
+ ufshcd_remove(hba);
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem_res)
+ dev_err(&pdev->dev, "ufshcd: Memory resource not available\n");
+ else {
+ mem_size = resource_size(mem_res);
+ release_mem_region(mem_res->start, mem_size);
+ }
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static const struct of_device_id ufs_of_match[] = {
+ { .compatible = "jedec,ufs-1.1"},
+};
+
+static const struct dev_pm_ops ufshcd_dev_pm_ops = {
+ .suspend = ufshcd_pltfrm_suspend,
+ .resume = ufshcd_pltfrm_resume,
+};
+
+static struct platform_driver ufshcd_pltfrm_driver = {
+ .probe = ufshcd_pltfrm_probe,
+ .remove = ufshcd_pltfrm_remove,
+ .driver = {
+ .name = "ufshcd",
+ .owner = THIS_MODULE,
+ .pm = &ufshcd_dev_pm_ops,
+ .of_match_table = ufs_of_match,
+ },
+};
+
+module_platform_driver(ufshcd_pltfrm_driver);
+
+MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
+MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
+MODULE_DESCRIPTION("UFS host controller Pltform bus based glue driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(UFSHCD_DRIVER_VERSION);
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 60fd40c4e4c2..c32a478df81b 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -478,7 +478,7 @@ static void ufshcd_compose_upiu(struct ufshcd_lrb *lrbp)
ucd_cmd_ptr->header.dword_2 = 0;
ucd_cmd_ptr->exp_data_transfer_len =
- cpu_to_be32(lrbp->cmd->transfersize);
+ cpu_to_be32(lrbp->cmd->sdb.length);
memcpy(ucd_cmd_ptr->cdb,
lrbp->cmd->cmnd,
diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c
index ef2e08e9b590..5dc9c4bfa66e 100644
--- a/drivers/tty/serial/68328serial.c
+++ b/drivers/tty/serial/68328serial.c
@@ -14,7 +14,6 @@
* 2.4/2.5 port David McCullough
*/
-#include <asm/dbg.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/serial.h>
diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c
index 52a3ecd40421..6fa2ae77fffd 100644
--- a/drivers/tty/serial/bcm63xx_uart.c
+++ b/drivers/tty/serial/bcm63xx_uart.c
@@ -30,7 +30,6 @@
#include <linux/serial.h>
#include <linux/serial_core.h>
-#include <bcm63xx_clk.h>
#include <bcm63xx_irq.h>
#include <bcm63xx_regs.h>
#include <bcm63xx_io.h>
diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c
index 6953dc82850c..a4fdce74f883 100644
--- a/drivers/tty/tty_audit.c
+++ b/drivers/tty/tty_audit.c
@@ -60,24 +60,22 @@ static void tty_audit_buf_put(struct tty_audit_buf *buf)
tty_audit_buf_free(buf);
}
-static void tty_audit_log(const char *description, struct task_struct *tsk,
- kuid_t loginuid, unsigned sessionid, int major,
- int minor, unsigned char *data, size_t size)
+static void tty_audit_log(const char *description, int major, int minor,
+ unsigned char *data, size_t size)
{
struct audit_buffer *ab;
+ struct task_struct *tsk = current;
+ uid_t uid = from_kuid(&init_user_ns, task_uid(tsk));
+ uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(tsk));
+ u32 sessionid = audit_get_sessionid(tsk);
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY);
if (ab) {
char name[sizeof(tsk->comm)];
- kuid_t uid = task_uid(tsk);
-
- audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u "
- "major=%d minor=%d comm=", description,
- tsk->pid,
- from_kuid(&init_user_ns, uid),
- from_kuid(&init_user_ns, loginuid),
- sessionid,
- major, minor);
+
+ audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d"
+ " minor=%d comm=", description, tsk->pid, uid,
+ loginuid, sessionid, major, minor);
get_task_comm(name, tsk);
audit_log_untrustedstring(ab, name);
audit_log_format(ab, " data=");
@@ -90,11 +88,9 @@ static void tty_audit_log(const char *description, struct task_struct *tsk,
* tty_audit_buf_push - Push buffered data out
*
* Generate an audit message from the contents of @buf, which is owned by
- * @tsk with @loginuid. @buf->mutex must be locked.
+ * the current task. @buf->mutex must be locked.
*/
-static void tty_audit_buf_push(struct task_struct *tsk, kuid_t loginuid,
- unsigned int sessionid,
- struct tty_audit_buf *buf)
+static void tty_audit_buf_push(struct tty_audit_buf *buf)
{
if (buf->valid == 0)
return;
@@ -102,25 +98,11 @@ static void tty_audit_buf_push(struct task_struct *tsk, kuid_t loginuid,
buf->valid = 0;
return;
}
- tty_audit_log("tty", tsk, loginuid, sessionid, buf->major, buf->minor,
- buf->data, buf->valid);
+ tty_audit_log("tty", buf->major, buf->minor, buf->data, buf->valid);
buf->valid = 0;
}
/**
- * tty_audit_buf_push_current - Push buffered data out
- *
- * Generate an audit message from the contents of @buf, which is owned by
- * the current task. @buf->mutex must be locked.
- */
-static void tty_audit_buf_push_current(struct tty_audit_buf *buf)
-{
- kuid_t auid = audit_get_loginuid(current);
- unsigned int sessionid = audit_get_sessionid(current);
- tty_audit_buf_push(current, auid, sessionid, buf);
-}
-
-/**
* tty_audit_exit - Handle a task exit
*
* Make sure all buffered data is written out and deallocate the buffer.
@@ -130,15 +112,13 @@ void tty_audit_exit(void)
{
struct tty_audit_buf *buf;
- spin_lock_irq(&current->sighand->siglock);
buf = current->signal->tty_audit_buf;
current->signal->tty_audit_buf = NULL;
- spin_unlock_irq(&current->sighand->siglock);
if (!buf)
return;
mutex_lock(&buf->mutex);
- tty_audit_buf_push_current(buf);
+ tty_audit_buf_push(buf);
mutex_unlock(&buf->mutex);
tty_audit_buf_put(buf);
@@ -151,9 +131,8 @@ void tty_audit_exit(void)
*/
void tty_audit_fork(struct signal_struct *sig)
{
- spin_lock_irq(&current->sighand->siglock);
sig->audit_tty = current->signal->audit_tty;
- spin_unlock_irq(&current->sighand->siglock);
+ sig->audit_tty_log_passwd = current->signal->audit_tty_log_passwd;
}
/**
@@ -163,20 +142,21 @@ void tty_audit_tiocsti(struct tty_struct *tty, char ch)
{
struct tty_audit_buf *buf;
int major, minor, should_audit;
+ unsigned long flags;
- spin_lock_irq(&current->sighand->siglock);
+ spin_lock_irqsave(&current->sighand->siglock, flags);
should_audit = current->signal->audit_tty;
buf = current->signal->tty_audit_buf;
if (buf)
atomic_inc(&buf->count);
- spin_unlock_irq(&current->sighand->siglock);
+ spin_unlock_irqrestore(&current->sighand->siglock, flags);
major = tty->driver->major;
minor = tty->driver->minor_start + tty->index;
if (buf) {
mutex_lock(&buf->mutex);
if (buf->major == major && buf->minor == minor)
- tty_audit_buf_push_current(buf);
+ tty_audit_buf_push(buf);
mutex_unlock(&buf->mutex);
tty_audit_buf_put(buf);
}
@@ -187,24 +167,20 @@ void tty_audit_tiocsti(struct tty_struct *tty, char ch)
auid = audit_get_loginuid(current);
sessionid = audit_get_sessionid(current);
- tty_audit_log("ioctl=TIOCSTI", current, auid, sessionid, major,
- minor, &ch, 1);
+ tty_audit_log("ioctl=TIOCSTI", major, minor, &ch, 1);
}
}
/**
- * tty_audit_push_task - Flush task's pending audit data
- * @tsk: task pointer
- * @loginuid: sender login uid
- * @sessionid: sender session id
+ * tty_audit_push_current - Flush current's pending audit data
*
- * Called with a ref on @tsk held. Try to lock sighand and get a
- * reference to the tty audit buffer if available.
+ * Try to lock sighand and get a reference to the tty audit buffer if available.
* Flush the buffer or return an appropriate error code.
*/
-int tty_audit_push_task(struct task_struct *tsk, kuid_t loginuid, u32 sessionid)
+int tty_audit_push_current(void)
{
struct tty_audit_buf *buf = ERR_PTR(-EPERM);
+ struct task_struct *tsk = current;
unsigned long flags;
if (!lock_task_sighand(tsk, &flags))
@@ -225,7 +201,7 @@ int tty_audit_push_task(struct task_struct *tsk, kuid_t loginuid, u32 sessionid)
return PTR_ERR(buf);
mutex_lock(&buf->mutex);
- tty_audit_buf_push(tsk, loginuid, sessionid, buf);
+ tty_audit_buf_push(buf);
mutex_unlock(&buf->mutex);
tty_audit_buf_put(buf);
@@ -243,10 +219,11 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty,
unsigned icanon)
{
struct tty_audit_buf *buf, *buf2;
+ unsigned long flags;
buf = NULL;
buf2 = NULL;
- spin_lock_irq(&current->sighand->siglock);
+ spin_lock_irqsave(&current->sighand->siglock, flags);
if (likely(!current->signal->audit_tty))
goto out;
buf = current->signal->tty_audit_buf;
@@ -254,7 +231,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty,
atomic_inc(&buf->count);
goto out;
}
- spin_unlock_irq(&current->sighand->siglock);
+ spin_unlock_irqrestore(&current->sighand->siglock, flags);
buf2 = tty_audit_buf_alloc(tty->driver->major,
tty->driver->minor_start + tty->index,
@@ -264,7 +241,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty,
return NULL;
}
- spin_lock_irq(&current->sighand->siglock);
+ spin_lock_irqsave(&current->sighand->siglock, flags);
if (!current->signal->audit_tty)
goto out;
buf = current->signal->tty_audit_buf;
@@ -276,7 +253,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty,
atomic_inc(&buf->count);
/* Fall through */
out:
- spin_unlock_irq(&current->sighand->siglock);
+ spin_unlock_irqrestore(&current->sighand->siglock, flags);
if (buf2)
tty_audit_buf_free(buf2);
return buf;
@@ -292,10 +269,18 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data,
{
struct tty_audit_buf *buf;
int major, minor;
+ int audit_log_tty_passwd;
+ unsigned long flags;
if (unlikely(size == 0))
return;
+ spin_lock_irqsave(&current->sighand->siglock, flags);
+ audit_log_tty_passwd = current->signal->audit_tty_log_passwd;
+ spin_unlock_irqrestore(&current->sighand->siglock, flags);
+ if (!audit_log_tty_passwd && icanon && !L_ECHO(tty))
+ return;
+
if (tty->driver->type == TTY_DRIVER_TYPE_PTY
&& tty->driver->subtype == PTY_TYPE_MASTER)
return;
@@ -309,7 +294,7 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data,
minor = tty->driver->minor_start + tty->index;
if (buf->major != major || buf->minor != minor
|| buf->icanon != icanon) {
- tty_audit_buf_push_current(buf);
+ tty_audit_buf_push(buf);
buf->major = major;
buf->minor = minor;
buf->icanon = icanon;
@@ -325,7 +310,7 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data,
data += run;
size -= run;
if (buf->valid == N_TTY_BUF_SIZE)
- tty_audit_buf_push_current(buf);
+ tty_audit_buf_push(buf);
} while (size != 0);
mutex_unlock(&buf->mutex);
tty_audit_buf_put(buf);
@@ -339,16 +324,17 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data,
void tty_audit_push(struct tty_struct *tty)
{
struct tty_audit_buf *buf;
+ unsigned long flags;
- spin_lock_irq(&current->sighand->siglock);
+ spin_lock_irqsave(&current->sighand->siglock, flags);
if (likely(!current->signal->audit_tty)) {
- spin_unlock_irq(&current->sighand->siglock);
+ spin_unlock_irqrestore(&current->sighand->siglock, flags);
return;
}
buf = current->signal->tty_audit_buf;
if (buf)
atomic_inc(&buf->count);
- spin_unlock_irq(&current->sighand->siglock);
+ spin_unlock_irqrestore(&current->sighand->siglock, flags);
if (buf) {
int major, minor;
@@ -357,7 +343,7 @@ void tty_audit_push(struct tty_struct *tty)
minor = tty->driver->minor_start + tty->index;
mutex_lock(&buf->mutex);
if (buf->major == major && buf->minor == minor)
- tty_audit_buf_push_current(buf);
+ tty_audit_buf_push(buf);
mutex_unlock(&buf->mutex);
tty_audit_buf_put(buf);
}
diff --git a/drivers/video/au1100fb.c b/drivers/video/au1100fb.c
index ddabaa867b0d..700cac067b46 100644
--- a/drivers/video/au1100fb.c
+++ b/drivers/video/au1100fb.c
@@ -111,30 +111,16 @@ static int au1100fb_fb_blank(int blank_mode, struct fb_info *fbi)
switch (blank_mode) {
case VESA_NO_BLANKING:
- /* Turn on panel */
- fbdev->regs->lcd_control |= LCD_CONTROL_GO;
-#ifdef CONFIG_MIPS_PB1100
- if (fbdev->panel_idx == 1) {
- au_writew(au_readw(PB1100_G_CONTROL)
- | (PB1100_G_CONTROL_BL | PB1100_G_CONTROL_VDD),
- PB1100_G_CONTROL);
- }
-#endif
+ /* Turn on panel */
+ fbdev->regs->lcd_control |= LCD_CONTROL_GO;
au_sync();
break;
case VESA_VSYNC_SUSPEND:
case VESA_HSYNC_SUSPEND:
case VESA_POWERDOWN:
- /* Turn off panel */
- fbdev->regs->lcd_control &= ~LCD_CONTROL_GO;
-#ifdef CONFIG_MIPS_PB1100
- if (fbdev->panel_idx == 1) {
- au_writew(au_readw(PB1100_G_CONTROL)
- & ~(PB1100_G_CONTROL_BL | PB1100_G_CONTROL_VDD),
- PB1100_G_CONTROL);
- }
-#endif
+ /* Turn off panel */
+ fbdev->regs->lcd_control &= ~LCD_CONTROL_GO;
au_sync();
break;
default:
diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig
index dd4d9cb86243..f03bf501527f 100644
--- a/drivers/xen/Kconfig
+++ b/drivers/xen/Kconfig
@@ -141,7 +141,7 @@ config XEN_GRANT_DEV_ALLOC
config SWIOTLB_XEN
def_bool y
- depends on PCI
+ depends on PCI && X86
select SWIOTLB
config XEN_TMEM
diff --git a/drivers/xen/events.c b/drivers/xen/events.c
index d8cc8127f19c..6a6bbe4ede92 100644
--- a/drivers/xen/events.c
+++ b/drivers/xen/events.c
@@ -167,6 +167,8 @@ static void xen_irq_info_common_init(struct irq_info *info,
info->cpu = cpu;
evtchn_to_irq[evtchn] = irq;
+
+ irq_clear_status_flags(irq, IRQ_NOREQUEST|IRQ_NOAUTOEN);
}
static void xen_irq_info_evtchn_init(unsigned irq,
@@ -874,7 +876,6 @@ int bind_evtchn_to_irq(unsigned int evtchn)
struct irq_info *info = info_for_irq(irq);
WARN_ON(info == NULL || info->type != IRQT_EVTCHN);
}
- irq_clear_status_flags(irq, IRQ_NOREQUEST|IRQ_NOAUTOEN);
out:
mutex_unlock(&irq_mapping_update_lock);