162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *			Linux MegaRAID device driver
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (c) 2003-2004  LSI Logic Corporation.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * FILE		: megaraid_mbox.c
962306a36Sopenharmony_ci * Version	: v2.20.5.1 (Nov 16 2006)
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Authors:
1262306a36Sopenharmony_ci * 	Atul Mukker		<Atul.Mukker@lsi.com>
1362306a36Sopenharmony_ci * 	Sreenivas Bagalkote	<Sreenivas.Bagalkote@lsi.com>
1462306a36Sopenharmony_ci * 	Manoj Jose		<Manoj.Jose@lsi.com>
1562306a36Sopenharmony_ci * 	Seokmann Ju
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * List of supported controllers
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * OEM	Product Name			VID	DID	SSVID	SSID
2062306a36Sopenharmony_ci * ---	------------			---	---	----	----
2162306a36Sopenharmony_ci * Dell PERC3/QC			101E	1960	1028	0471
2262306a36Sopenharmony_ci * Dell PERC3/DC			101E	1960	1028	0493
2362306a36Sopenharmony_ci * Dell PERC3/SC			101E	1960	1028	0475
2462306a36Sopenharmony_ci * Dell PERC3/Di			1028	1960	1028	0123
2562306a36Sopenharmony_ci * Dell PERC4/SC			1000	1960	1028	0520
2662306a36Sopenharmony_ci * Dell PERC4/DC			1000	1960	1028	0518
2762306a36Sopenharmony_ci * Dell PERC4/QC			1000	0407	1028	0531
2862306a36Sopenharmony_ci * Dell PERC4/Di			1028	000F	1028	014A
2962306a36Sopenharmony_ci * Dell PERC 4e/Si			1028	0013	1028	016c
3062306a36Sopenharmony_ci * Dell PERC 4e/Di			1028	0013	1028	016d
3162306a36Sopenharmony_ci * Dell PERC 4e/Di			1028	0013	1028	016e
3262306a36Sopenharmony_ci * Dell PERC 4e/Di			1028	0013	1028	016f
3362306a36Sopenharmony_ci * Dell PERC 4e/Di			1028	0013	1028	0170
3462306a36Sopenharmony_ci * Dell PERC 4e/DC			1000	0408	1028	0002
3562306a36Sopenharmony_ci * Dell PERC 4e/SC			1000	0408	1028	0001
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * LSI MegaRAID SCSI 320-0		1000	1960	1000	A520
3862306a36Sopenharmony_ci * LSI MegaRAID SCSI 320-1		1000	1960	1000	0520
3962306a36Sopenharmony_ci * LSI MegaRAID SCSI 320-2		1000	1960	1000	0518
4062306a36Sopenharmony_ci * LSI MegaRAID SCSI 320-0X		1000	0407	1000	0530
4162306a36Sopenharmony_ci * LSI MegaRAID SCSI 320-2X		1000	0407	1000	0532
4262306a36Sopenharmony_ci * LSI MegaRAID SCSI 320-4X		1000	0407	1000	0531
4362306a36Sopenharmony_ci * LSI MegaRAID SCSI 320-1E		1000	0408	1000	0001
4462306a36Sopenharmony_ci * LSI MegaRAID SCSI 320-2E		1000	0408	1000	0002
4562306a36Sopenharmony_ci * LSI MegaRAID SATA 150-4		1000	1960	1000	4523
4662306a36Sopenharmony_ci * LSI MegaRAID SATA 150-6		1000	1960	1000	0523
4762306a36Sopenharmony_ci * LSI MegaRAID SATA 300-4X		1000	0409	1000	3004
4862306a36Sopenharmony_ci * LSI MegaRAID SATA 300-8X		1000	0409	1000	3008
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * INTEL RAID Controller SRCU42X	1000	0407	8086	0532
5162306a36Sopenharmony_ci * INTEL RAID Controller SRCS16		1000	1960	8086	0523
5262306a36Sopenharmony_ci * INTEL RAID Controller SRCU42E	1000	0408	8086	0002
5362306a36Sopenharmony_ci * INTEL RAID Controller SRCZCRX	1000	0407	8086	0530
5462306a36Sopenharmony_ci * INTEL RAID Controller SRCS28X	1000	0409	8086	3008
5562306a36Sopenharmony_ci * INTEL RAID Controller SROMBU42E	1000	0408	8086	3431
5662306a36Sopenharmony_ci * INTEL RAID Controller SROMBU42E	1000	0408	8086	3499
5762306a36Sopenharmony_ci * INTEL RAID Controller SRCU51L	1000	1960	8086	0520
5862306a36Sopenharmony_ci *
5962306a36Sopenharmony_ci * FSC	MegaRAID PCI Express ROMB	1000	0408	1734	1065
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci * ACER	MegaRAID ROMB-2E		1000	0408	1025	004D
6262306a36Sopenharmony_ci *
6362306a36Sopenharmony_ci * NEC	MegaRAID PCI Express ROMB	1000	0408	1033	8287
6462306a36Sopenharmony_ci *
6562306a36Sopenharmony_ci * For history of changes, see Documentation/scsi/ChangeLog.megaraid
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#include <linux/slab.h>
6962306a36Sopenharmony_ci#include <linux/module.h>
7062306a36Sopenharmony_ci#include "megaraid_mbox.h"
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int megaraid_init(void);
7362306a36Sopenharmony_cistatic void megaraid_exit(void);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int megaraid_probe_one(struct pci_dev*, const struct pci_device_id *);
7662306a36Sopenharmony_cistatic void megaraid_detach_one(struct pci_dev *);
7762306a36Sopenharmony_cistatic void megaraid_mbox_shutdown(struct pci_dev *);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic int megaraid_io_attach(adapter_t *);
8062306a36Sopenharmony_cistatic void megaraid_io_detach(adapter_t *);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic int megaraid_init_mbox(adapter_t *);
8362306a36Sopenharmony_cistatic void megaraid_fini_mbox(adapter_t *);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic int megaraid_alloc_cmd_packets(adapter_t *);
8662306a36Sopenharmony_cistatic void megaraid_free_cmd_packets(adapter_t *);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int megaraid_mbox_setup_dma_pools(adapter_t *);
8962306a36Sopenharmony_cistatic void megaraid_mbox_teardown_dma_pools(adapter_t *);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int megaraid_sysfs_alloc_resources(adapter_t *);
9262306a36Sopenharmony_cistatic void megaraid_sysfs_free_resources(adapter_t *);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int megaraid_abort_handler(struct scsi_cmnd *);
9562306a36Sopenharmony_cistatic int megaraid_reset_handler(struct scsi_cmnd *);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic int mbox_post_sync_cmd(adapter_t *, uint8_t []);
9862306a36Sopenharmony_cistatic int mbox_post_sync_cmd_fast(adapter_t *, uint8_t []);
9962306a36Sopenharmony_cistatic int megaraid_busywait_mbox(mraid_device_t *);
10062306a36Sopenharmony_cistatic int megaraid_mbox_product_info(adapter_t *);
10162306a36Sopenharmony_cistatic int megaraid_mbox_extended_cdb(adapter_t *);
10262306a36Sopenharmony_cistatic int megaraid_mbox_support_ha(adapter_t *, uint16_t *);
10362306a36Sopenharmony_cistatic int megaraid_mbox_support_random_del(adapter_t *);
10462306a36Sopenharmony_cistatic int megaraid_mbox_get_max_sg(adapter_t *);
10562306a36Sopenharmony_cistatic void megaraid_mbox_enum_raid_scsi(adapter_t *);
10662306a36Sopenharmony_cistatic void megaraid_mbox_flush_cache(adapter_t *);
10762306a36Sopenharmony_cistatic int megaraid_mbox_fire_sync_cmd(adapter_t *);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic void megaraid_mbox_display_scb(adapter_t *, scb_t *);
11062306a36Sopenharmony_cistatic void megaraid_mbox_setup_device_map(adapter_t *);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int megaraid_queue_command(struct Scsi_Host *, struct scsi_cmnd *);
11362306a36Sopenharmony_cistatic scb_t *megaraid_mbox_build_cmd(adapter_t *, struct scsi_cmnd *, int *);
11462306a36Sopenharmony_cistatic void megaraid_mbox_runpendq(adapter_t *, scb_t *);
11562306a36Sopenharmony_cistatic void megaraid_mbox_prepare_pthru(adapter_t *, scb_t *,
11662306a36Sopenharmony_ci		struct scsi_cmnd *);
11762306a36Sopenharmony_cistatic void megaraid_mbox_prepare_epthru(adapter_t *, scb_t *,
11862306a36Sopenharmony_ci		struct scsi_cmnd *);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic irqreturn_t megaraid_isr(int, void *);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic void megaraid_mbox_dpc(unsigned long);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic ssize_t megaraid_mbox_app_hndl_show(struct device *, struct device_attribute *attr, char *);
12562306a36Sopenharmony_cistatic ssize_t megaraid_mbox_ld_show(struct device *, struct device_attribute *attr, char *);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic int megaraid_cmm_register(adapter_t *);
12862306a36Sopenharmony_cistatic int megaraid_cmm_unregister(adapter_t *);
12962306a36Sopenharmony_cistatic int megaraid_mbox_mm_handler(unsigned long, uioc_t *, uint32_t);
13062306a36Sopenharmony_cistatic int megaraid_mbox_mm_command(adapter_t *, uioc_t *);
13162306a36Sopenharmony_cistatic void megaraid_mbox_mm_done(adapter_t *, scb_t *);
13262306a36Sopenharmony_cistatic int gather_hbainfo(adapter_t *, mraid_hba_info_t *);
13362306a36Sopenharmony_cistatic int wait_till_fw_empty(adapter_t *);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ciMODULE_AUTHOR("megaraidlinux@lsi.com");
13862306a36Sopenharmony_ciMODULE_DESCRIPTION("LSI Logic MegaRAID Mailbox Driver");
13962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
14062306a36Sopenharmony_ciMODULE_VERSION(MEGARAID_VERSION);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/*
14362306a36Sopenharmony_ci * ### modules parameters for driver ###
14462306a36Sopenharmony_ci */
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci/*
14762306a36Sopenharmony_ci * Set to enable driver to expose unconfigured disk to kernel
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_cistatic int megaraid_expose_unconf_disks = 0;
15062306a36Sopenharmony_cimodule_param_named(unconf_disks, megaraid_expose_unconf_disks, int, 0);
15162306a36Sopenharmony_ciMODULE_PARM_DESC(unconf_disks,
15262306a36Sopenharmony_ci	"Set to expose unconfigured disks to kernel (default=0)");
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/*
15562306a36Sopenharmony_ci * driver wait time if the adapter's mailbox is busy
15662306a36Sopenharmony_ci */
15762306a36Sopenharmony_cistatic unsigned int max_mbox_busy_wait = MBOX_BUSY_WAIT;
15862306a36Sopenharmony_cimodule_param_named(busy_wait, max_mbox_busy_wait, int, 0);
15962306a36Sopenharmony_ciMODULE_PARM_DESC(busy_wait,
16062306a36Sopenharmony_ci	"Max wait for mailbox in microseconds if busy (default=10)");
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/*
16362306a36Sopenharmony_ci * number of sectors per IO command
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_cistatic unsigned int megaraid_max_sectors = MBOX_MAX_SECTORS;
16662306a36Sopenharmony_cimodule_param_named(max_sectors, megaraid_max_sectors, int, 0);
16762306a36Sopenharmony_ciMODULE_PARM_DESC(max_sectors,
16862306a36Sopenharmony_ci	"Maximum number of sectors per IO command (default=128)");
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci/*
17162306a36Sopenharmony_ci * number of commands per logical unit
17262306a36Sopenharmony_ci */
17362306a36Sopenharmony_cistatic unsigned int megaraid_cmd_per_lun = MBOX_DEF_CMD_PER_LUN;
17462306a36Sopenharmony_cimodule_param_named(cmd_per_lun, megaraid_cmd_per_lun, int, 0);
17562306a36Sopenharmony_ciMODULE_PARM_DESC(cmd_per_lun,
17662306a36Sopenharmony_ci	"Maximum number of commands per logical unit (default=64)");
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci/*
18062306a36Sopenharmony_ci * Fast driver load option, skip scanning for physical devices during load.
18162306a36Sopenharmony_ci * This would result in non-disk devices being skipped during driver load
18262306a36Sopenharmony_ci * time. These can be later added though, using /proc/scsi/scsi
18362306a36Sopenharmony_ci */
18462306a36Sopenharmony_cistatic unsigned int megaraid_fast_load;
18562306a36Sopenharmony_cimodule_param_named(fast_load, megaraid_fast_load, int, 0);
18662306a36Sopenharmony_ciMODULE_PARM_DESC(fast_load,
18762306a36Sopenharmony_ci	"Faster loading of the driver, skips physical devices! (default=0)");
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/*
19162306a36Sopenharmony_ci * mraid_debug level - threshold for amount of information to be displayed by
19262306a36Sopenharmony_ci * the driver. This level can be changed through modules parameters, ioctl or
19362306a36Sopenharmony_ci * sysfs/proc interface. By default, print the announcement messages only.
19462306a36Sopenharmony_ci */
19562306a36Sopenharmony_ciint mraid_debug_level = CL_ANN;
19662306a36Sopenharmony_cimodule_param_named(debug_level, mraid_debug_level, int, 0);
19762306a36Sopenharmony_ciMODULE_PARM_DESC(debug_level, "Debug level for driver (default=0)");
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci/*
20062306a36Sopenharmony_ci * PCI table for all supported controllers.
20162306a36Sopenharmony_ci */
20262306a36Sopenharmony_cistatic struct pci_device_id pci_id_table_g[] =  {
20362306a36Sopenharmony_ci	{
20462306a36Sopenharmony_ci		PCI_VENDOR_ID_DELL,
20562306a36Sopenharmony_ci		PCI_DEVICE_ID_PERC4_DI_DISCOVERY,
20662306a36Sopenharmony_ci		PCI_VENDOR_ID_DELL,
20762306a36Sopenharmony_ci		PCI_SUBSYS_ID_PERC4_DI_DISCOVERY,
20862306a36Sopenharmony_ci	},
20962306a36Sopenharmony_ci	{
21062306a36Sopenharmony_ci		PCI_VENDOR_ID_LSI_LOGIC,
21162306a36Sopenharmony_ci		PCI_DEVICE_ID_PERC4_SC,
21262306a36Sopenharmony_ci		PCI_VENDOR_ID_DELL,
21362306a36Sopenharmony_ci		PCI_SUBSYS_ID_PERC4_SC,
21462306a36Sopenharmony_ci	},
21562306a36Sopenharmony_ci	{
21662306a36Sopenharmony_ci		PCI_VENDOR_ID_LSI_LOGIC,
21762306a36Sopenharmony_ci		PCI_DEVICE_ID_PERC4_DC,
21862306a36Sopenharmony_ci		PCI_VENDOR_ID_DELL,
21962306a36Sopenharmony_ci		PCI_SUBSYS_ID_PERC4_DC,
22062306a36Sopenharmony_ci	},
22162306a36Sopenharmony_ci	{
22262306a36Sopenharmony_ci		PCI_VENDOR_ID_LSI_LOGIC,
22362306a36Sopenharmony_ci		PCI_DEVICE_ID_VERDE,
22462306a36Sopenharmony_ci		PCI_ANY_ID,
22562306a36Sopenharmony_ci		PCI_ANY_ID,
22662306a36Sopenharmony_ci	},
22762306a36Sopenharmony_ci	{
22862306a36Sopenharmony_ci		PCI_VENDOR_ID_DELL,
22962306a36Sopenharmony_ci		PCI_DEVICE_ID_PERC4_DI_EVERGLADES,
23062306a36Sopenharmony_ci		PCI_VENDOR_ID_DELL,
23162306a36Sopenharmony_ci		PCI_SUBSYS_ID_PERC4_DI_EVERGLADES,
23262306a36Sopenharmony_ci	},
23362306a36Sopenharmony_ci	{
23462306a36Sopenharmony_ci		PCI_VENDOR_ID_DELL,
23562306a36Sopenharmony_ci		PCI_DEVICE_ID_PERC4E_SI_BIGBEND,
23662306a36Sopenharmony_ci		PCI_VENDOR_ID_DELL,
23762306a36Sopenharmony_ci		PCI_SUBSYS_ID_PERC4E_SI_BIGBEND,
23862306a36Sopenharmony_ci	},
23962306a36Sopenharmony_ci	{
24062306a36Sopenharmony_ci		PCI_VENDOR_ID_DELL,
24162306a36Sopenharmony_ci		PCI_DEVICE_ID_PERC4E_DI_KOBUK,
24262306a36Sopenharmony_ci		PCI_VENDOR_ID_DELL,
24362306a36Sopenharmony_ci		PCI_SUBSYS_ID_PERC4E_DI_KOBUK,
24462306a36Sopenharmony_ci	},
24562306a36Sopenharmony_ci	{
24662306a36Sopenharmony_ci		PCI_VENDOR_ID_DELL,
24762306a36Sopenharmony_ci		PCI_DEVICE_ID_PERC4E_DI_CORVETTE,
24862306a36Sopenharmony_ci		PCI_VENDOR_ID_DELL,
24962306a36Sopenharmony_ci		PCI_SUBSYS_ID_PERC4E_DI_CORVETTE,
25062306a36Sopenharmony_ci	},
25162306a36Sopenharmony_ci	{
25262306a36Sopenharmony_ci		PCI_VENDOR_ID_DELL,
25362306a36Sopenharmony_ci		PCI_DEVICE_ID_PERC4E_DI_EXPEDITION,
25462306a36Sopenharmony_ci		PCI_VENDOR_ID_DELL,
25562306a36Sopenharmony_ci		PCI_SUBSYS_ID_PERC4E_DI_EXPEDITION,
25662306a36Sopenharmony_ci	},
25762306a36Sopenharmony_ci	{
25862306a36Sopenharmony_ci		PCI_VENDOR_ID_DELL,
25962306a36Sopenharmony_ci		PCI_DEVICE_ID_PERC4E_DI_GUADALUPE,
26062306a36Sopenharmony_ci		PCI_VENDOR_ID_DELL,
26162306a36Sopenharmony_ci		PCI_SUBSYS_ID_PERC4E_DI_GUADALUPE,
26262306a36Sopenharmony_ci	},
26362306a36Sopenharmony_ci	{
26462306a36Sopenharmony_ci		PCI_VENDOR_ID_LSI_LOGIC,
26562306a36Sopenharmony_ci		PCI_DEVICE_ID_DOBSON,
26662306a36Sopenharmony_ci		PCI_ANY_ID,
26762306a36Sopenharmony_ci		PCI_ANY_ID,
26862306a36Sopenharmony_ci	},
26962306a36Sopenharmony_ci	{
27062306a36Sopenharmony_ci		PCI_VENDOR_ID_AMI,
27162306a36Sopenharmony_ci		PCI_DEVICE_ID_AMI_MEGARAID3,
27262306a36Sopenharmony_ci		PCI_ANY_ID,
27362306a36Sopenharmony_ci		PCI_ANY_ID,
27462306a36Sopenharmony_ci	},
27562306a36Sopenharmony_ci	{
27662306a36Sopenharmony_ci		PCI_VENDOR_ID_LSI_LOGIC,
27762306a36Sopenharmony_ci		PCI_DEVICE_ID_AMI_MEGARAID3,
27862306a36Sopenharmony_ci		PCI_ANY_ID,
27962306a36Sopenharmony_ci		PCI_ANY_ID,
28062306a36Sopenharmony_ci	},
28162306a36Sopenharmony_ci	{
28262306a36Sopenharmony_ci		PCI_VENDOR_ID_LSI_LOGIC,
28362306a36Sopenharmony_ci		PCI_DEVICE_ID_LINDSAY,
28462306a36Sopenharmony_ci		PCI_ANY_ID,
28562306a36Sopenharmony_ci		PCI_ANY_ID,
28662306a36Sopenharmony_ci	},
28762306a36Sopenharmony_ci	{0}	/* Terminating entry */
28862306a36Sopenharmony_ci};
28962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pci_id_table_g);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic struct pci_driver megaraid_pci_driver = {
29362306a36Sopenharmony_ci	.name		= "megaraid",
29462306a36Sopenharmony_ci	.id_table	= pci_id_table_g,
29562306a36Sopenharmony_ci	.probe		= megaraid_probe_one,
29662306a36Sopenharmony_ci	.remove		= megaraid_detach_one,
29762306a36Sopenharmony_ci	.shutdown	= megaraid_mbox_shutdown,
29862306a36Sopenharmony_ci};
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci// definitions for the device attributes for exporting logical drive number
30362306a36Sopenharmony_ci// for a scsi address (Host, Channel, Id, Lun)
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic DEVICE_ATTR_ADMIN_RO(megaraid_mbox_app_hndl);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci// Host template initializer for megaraid mbox sysfs device attributes
30862306a36Sopenharmony_cistatic struct attribute *megaraid_shost_attrs[] = {
30962306a36Sopenharmony_ci	&dev_attr_megaraid_mbox_app_hndl.attr,
31062306a36Sopenharmony_ci	NULL,
31162306a36Sopenharmony_ci};
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ciATTRIBUTE_GROUPS(megaraid_shost);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic DEVICE_ATTR_ADMIN_RO(megaraid_mbox_ld);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci// Host template initializer for megaraid mbox sysfs device attributes
31862306a36Sopenharmony_cistatic struct attribute *megaraid_sdev_attrs[] = {
31962306a36Sopenharmony_ci	&dev_attr_megaraid_mbox_ld.attr,
32062306a36Sopenharmony_ci	NULL,
32162306a36Sopenharmony_ci};
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ciATTRIBUTE_GROUPS(megaraid_sdev);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/*
32662306a36Sopenharmony_ci * Scsi host template for megaraid unified driver
32762306a36Sopenharmony_ci */
32862306a36Sopenharmony_cistatic const struct scsi_host_template megaraid_template_g = {
32962306a36Sopenharmony_ci	.module				= THIS_MODULE,
33062306a36Sopenharmony_ci	.name				= "LSI Logic MegaRAID driver",
33162306a36Sopenharmony_ci	.proc_name			= "megaraid",
33262306a36Sopenharmony_ci	.queuecommand			= megaraid_queue_command,
33362306a36Sopenharmony_ci	.eh_abort_handler		= megaraid_abort_handler,
33462306a36Sopenharmony_ci	.eh_host_reset_handler		= megaraid_reset_handler,
33562306a36Sopenharmony_ci	.change_queue_depth		= scsi_change_queue_depth,
33662306a36Sopenharmony_ci	.no_write_same			= 1,
33762306a36Sopenharmony_ci	.sdev_groups			= megaraid_sdev_groups,
33862306a36Sopenharmony_ci	.shost_groups			= megaraid_shost_groups,
33962306a36Sopenharmony_ci};
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci/**
34362306a36Sopenharmony_ci * megaraid_init - module load hook
34462306a36Sopenharmony_ci *
34562306a36Sopenharmony_ci * We register ourselves as hotplug enabled module and let PCI subsystem
34662306a36Sopenharmony_ci * discover our adapters.
34762306a36Sopenharmony_ci */
34862306a36Sopenharmony_cistatic int __init
34962306a36Sopenharmony_cimegaraid_init(void)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	int	rval;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	// Announce the driver version
35462306a36Sopenharmony_ci	con_log(CL_ANN, (KERN_INFO "megaraid: %s %s\n", MEGARAID_VERSION,
35562306a36Sopenharmony_ci		MEGARAID_EXT_VERSION));
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	// check validity of module parameters
35862306a36Sopenharmony_ci	if (megaraid_cmd_per_lun > MBOX_MAX_SCSI_CMDS) {
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
36162306a36Sopenharmony_ci			"megaraid mailbox: max commands per lun reset to %d\n",
36262306a36Sopenharmony_ci			MBOX_MAX_SCSI_CMDS));
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci		megaraid_cmd_per_lun = MBOX_MAX_SCSI_CMDS;
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	// register as a PCI hot-plug driver module
36962306a36Sopenharmony_ci	rval = pci_register_driver(&megaraid_pci_driver);
37062306a36Sopenharmony_ci	if (rval < 0) {
37162306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
37262306a36Sopenharmony_ci			"megaraid: could not register hotplug support.\n"));
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	return rval;
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci/**
38062306a36Sopenharmony_ci * megaraid_exit - driver unload entry point
38162306a36Sopenharmony_ci *
38262306a36Sopenharmony_ci * We simply unwrap the megaraid_init routine here.
38362306a36Sopenharmony_ci */
38462306a36Sopenharmony_cistatic void __exit
38562306a36Sopenharmony_cimegaraid_exit(void)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	con_log(CL_DLEVEL1, (KERN_NOTICE "megaraid: unloading framework\n"));
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	// unregister as PCI hotplug driver
39062306a36Sopenharmony_ci	pci_unregister_driver(&megaraid_pci_driver);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci/**
39762306a36Sopenharmony_ci * megaraid_probe_one - PCI hotplug entry point
39862306a36Sopenharmony_ci * @pdev	: handle to this controller's PCI configuration space
39962306a36Sopenharmony_ci * @id		: pci device id of the class of controllers
40062306a36Sopenharmony_ci *
40162306a36Sopenharmony_ci * This routine should be called whenever a new adapter is detected by the
40262306a36Sopenharmony_ci * PCI hotplug susbsystem.
40362306a36Sopenharmony_ci */
40462306a36Sopenharmony_cistatic int
40562306a36Sopenharmony_cimegaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	adapter_t	*adapter;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	// detected a new controller
41162306a36Sopenharmony_ci	con_log(CL_ANN, (KERN_INFO
41262306a36Sopenharmony_ci		"megaraid: probe new device %#4.04x:%#4.04x:%#4.04x:%#4.04x: ",
41362306a36Sopenharmony_ci		pdev->vendor, pdev->device, pdev->subsystem_vendor,
41462306a36Sopenharmony_ci		pdev->subsystem_device));
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	con_log(CL_ANN, ("bus %d:slot %d:func %d\n", pdev->bus->number,
41762306a36Sopenharmony_ci		PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)));
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (pci_enable_device(pdev)) {
42062306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
42162306a36Sopenharmony_ci				"megaraid: pci_enable_device failed\n"));
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci		return -ENODEV;
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	// Enable bus-mastering on this controller
42762306a36Sopenharmony_ci	pci_set_master(pdev);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	// Allocate the per driver initialization structure
43062306a36Sopenharmony_ci	adapter = kzalloc(sizeof(adapter_t), GFP_KERNEL);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (adapter == NULL) {
43362306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
43462306a36Sopenharmony_ci		"megaraid: out of memory, %s %d.\n", __func__, __LINE__));
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci		goto out_probe_one;
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	// set up PCI related soft state and other pre-known parameters
44162306a36Sopenharmony_ci	adapter->unique_id	= pci_dev_id(pdev);
44262306a36Sopenharmony_ci	adapter->irq		= pdev->irq;
44362306a36Sopenharmony_ci	adapter->pdev		= pdev;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	atomic_set(&adapter->being_detached, 0);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	// Setup the default DMA mask. This would be changed later on
44862306a36Sopenharmony_ci	// depending on hardware capabilities
44962306a36Sopenharmony_ci	if (dma_set_mask(&adapter->pdev->dev, DMA_BIT_MASK(32))) {
45062306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
45162306a36Sopenharmony_ci			"megaraid: dma_set_mask failed:%d\n", __LINE__));
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci		goto out_free_adapter;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	// Initialize the synchronization lock for kernel and LLD
45862306a36Sopenharmony_ci	spin_lock_init(&adapter->lock);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	// Initialize the command queues: the list of free SCBs and the list
46162306a36Sopenharmony_ci	// of pending SCBs.
46262306a36Sopenharmony_ci	INIT_LIST_HEAD(&adapter->kscb_pool);
46362306a36Sopenharmony_ci	spin_lock_init(SCSI_FREE_LIST_LOCK(adapter));
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	INIT_LIST_HEAD(&adapter->pend_list);
46662306a36Sopenharmony_ci	spin_lock_init(PENDING_LIST_LOCK(adapter));
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	INIT_LIST_HEAD(&adapter->completed_list);
46962306a36Sopenharmony_ci	spin_lock_init(COMPLETED_LIST_LOCK(adapter));
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	// Start the mailbox based controller
47362306a36Sopenharmony_ci	if (megaraid_init_mbox(adapter) != 0) {
47462306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
47562306a36Sopenharmony_ci			"megaraid: mailbox adapter did not initialize\n"));
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		goto out_free_adapter;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	// Register with LSI Common Management Module
48162306a36Sopenharmony_ci	if (megaraid_cmm_register(adapter) != 0) {
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
48462306a36Sopenharmony_ci		"megaraid: could not register with management module\n"));
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci		goto out_fini_mbox;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	// setup adapter handle in PCI soft state
49062306a36Sopenharmony_ci	pci_set_drvdata(pdev, adapter);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	// attach with scsi mid-layer
49362306a36Sopenharmony_ci	if (megaraid_io_attach(adapter) != 0) {
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING "megaraid: io attach failed\n"));
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		goto out_cmm_unreg;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	return 0;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ciout_cmm_unreg:
50362306a36Sopenharmony_ci	megaraid_cmm_unregister(adapter);
50462306a36Sopenharmony_ciout_fini_mbox:
50562306a36Sopenharmony_ci	megaraid_fini_mbox(adapter);
50662306a36Sopenharmony_ciout_free_adapter:
50762306a36Sopenharmony_ci	kfree(adapter);
50862306a36Sopenharmony_ciout_probe_one:
50962306a36Sopenharmony_ci	pci_disable_device(pdev);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	return -ENODEV;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci/**
51662306a36Sopenharmony_ci * megaraid_detach_one - release framework resources and call LLD release routine
51762306a36Sopenharmony_ci * @pdev	: handle for our PCI configuration space
51862306a36Sopenharmony_ci *
51962306a36Sopenharmony_ci * This routine is called during driver unload. We free all the allocated
52062306a36Sopenharmony_ci * resources and call the corresponding LLD so that it can also release all
52162306a36Sopenharmony_ci * its resources.
52262306a36Sopenharmony_ci *
52362306a36Sopenharmony_ci * This routine is also called from the PCI hotplug system.
52462306a36Sopenharmony_ci */
52562306a36Sopenharmony_cistatic void
52662306a36Sopenharmony_cimegaraid_detach_one(struct pci_dev *pdev)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	adapter_t		*adapter;
52962306a36Sopenharmony_ci	struct Scsi_Host	*host;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	// Start a rollback on this adapter
53362306a36Sopenharmony_ci	adapter = pci_get_drvdata(pdev);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (!adapter) {
53662306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_CRIT
53762306a36Sopenharmony_ci		"megaraid: Invalid detach on %#4.04x:%#4.04x:%#4.04x:%#4.04x\n",
53862306a36Sopenharmony_ci			pdev->vendor, pdev->device, pdev->subsystem_vendor,
53962306a36Sopenharmony_ci			pdev->subsystem_device));
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci		return;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci	else {
54462306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_NOTICE
54562306a36Sopenharmony_ci		"megaraid: detaching device %#4.04x:%#4.04x:%#4.04x:%#4.04x\n",
54662306a36Sopenharmony_ci			pdev->vendor, pdev->device, pdev->subsystem_vendor,
54762306a36Sopenharmony_ci			pdev->subsystem_device));
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	host = adapter->host;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	// do not allow any more requests from the management module for this
55462306a36Sopenharmony_ci	// adapter.
55562306a36Sopenharmony_ci	// FIXME: How do we account for the request which might still be
55662306a36Sopenharmony_ci	// pending with us?
55762306a36Sopenharmony_ci	atomic_set(&adapter->being_detached, 1);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	// detach from the IO sub-system
56062306a36Sopenharmony_ci	megaraid_io_detach(adapter);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	// Unregister from common management module
56362306a36Sopenharmony_ci	//
56462306a36Sopenharmony_ci	// FIXME: this must return success or failure for conditions if there
56562306a36Sopenharmony_ci	// is a command pending with LLD or not.
56662306a36Sopenharmony_ci	megaraid_cmm_unregister(adapter);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	// finalize the mailbox based controller and release all resources
56962306a36Sopenharmony_ci	megaraid_fini_mbox(adapter);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	kfree(adapter);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	scsi_host_put(host);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	pci_disable_device(pdev);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	return;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci/**
58262306a36Sopenharmony_ci * megaraid_mbox_shutdown - PCI shutdown for megaraid HBA
58362306a36Sopenharmony_ci * @pdev		: generic driver model device
58462306a36Sopenharmony_ci *
58562306a36Sopenharmony_ci * Shutdown notification, perform flush cache.
58662306a36Sopenharmony_ci */
58762306a36Sopenharmony_cistatic void
58862306a36Sopenharmony_cimegaraid_mbox_shutdown(struct pci_dev *pdev)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	adapter_t		*adapter = pci_get_drvdata(pdev);
59162306a36Sopenharmony_ci	static int		counter;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (!adapter) {
59462306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
59562306a36Sopenharmony_ci			"megaraid: null device in shutdown\n"));
59662306a36Sopenharmony_ci		return;
59762306a36Sopenharmony_ci	}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	// flush caches now
60062306a36Sopenharmony_ci	con_log(CL_ANN, (KERN_INFO "megaraid: flushing adapter %d...",
60162306a36Sopenharmony_ci		counter++));
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	megaraid_mbox_flush_cache(adapter);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	con_log(CL_ANN, ("done\n"));
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci/**
61062306a36Sopenharmony_ci * megaraid_io_attach - attach a device with the IO subsystem
61162306a36Sopenharmony_ci * @adapter		: controller's soft state
61262306a36Sopenharmony_ci *
61362306a36Sopenharmony_ci * Attach this device with the IO subsystem.
61462306a36Sopenharmony_ci */
61562306a36Sopenharmony_cistatic int
61662306a36Sopenharmony_cimegaraid_io_attach(adapter_t *adapter)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	struct Scsi_Host	*host;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	// Initialize SCSI Host structure
62162306a36Sopenharmony_ci	host = scsi_host_alloc(&megaraid_template_g, 8);
62262306a36Sopenharmony_ci	if (!host) {
62362306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
62462306a36Sopenharmony_ci			"megaraid mbox: scsi_register failed\n"));
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci		return -1;
62762306a36Sopenharmony_ci	}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	SCSIHOST2ADAP(host)	= (caddr_t)adapter;
63062306a36Sopenharmony_ci	adapter->host		= host;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	host->irq		= adapter->irq;
63362306a36Sopenharmony_ci	host->unique_id		= adapter->unique_id;
63462306a36Sopenharmony_ci	host->can_queue		= adapter->max_cmds;
63562306a36Sopenharmony_ci	host->this_id		= adapter->init_id;
63662306a36Sopenharmony_ci	host->sg_tablesize	= adapter->sglen;
63762306a36Sopenharmony_ci	host->max_sectors	= adapter->max_sectors;
63862306a36Sopenharmony_ci	host->cmd_per_lun	= adapter->cmd_per_lun;
63962306a36Sopenharmony_ci	host->max_channel	= adapter->max_channel;
64062306a36Sopenharmony_ci	host->max_id		= adapter->max_target;
64162306a36Sopenharmony_ci	host->max_lun		= adapter->max_lun;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	// notify mid-layer about the new controller
64562306a36Sopenharmony_ci	if (scsi_add_host(host, &adapter->pdev->dev)) {
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
64862306a36Sopenharmony_ci			"megaraid mbox: scsi_add_host failed\n"));
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		scsi_host_put(host);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci		return -1;
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	scsi_scan_host(host);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	return 0;
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci/**
66262306a36Sopenharmony_ci * megaraid_io_detach - detach a device from the IO subsystem
66362306a36Sopenharmony_ci * @adapter		: controller's soft state
66462306a36Sopenharmony_ci *
66562306a36Sopenharmony_ci * Detach this device from the IO subsystem.
66662306a36Sopenharmony_ci */
66762306a36Sopenharmony_cistatic void
66862306a36Sopenharmony_cimegaraid_io_detach(adapter_t *adapter)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	struct Scsi_Host	*host;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	con_log(CL_DLEVEL1, (KERN_INFO "megaraid: io detach\n"));
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	host = adapter->host;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	scsi_remove_host(host);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	return;
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci/*
68362306a36Sopenharmony_ci * START: Mailbox Low Level Driver
68462306a36Sopenharmony_ci *
68562306a36Sopenharmony_ci * This is section specific to the single mailbox based controllers
68662306a36Sopenharmony_ci */
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci/**
68962306a36Sopenharmony_ci * megaraid_init_mbox - initialize controller
69062306a36Sopenharmony_ci * @adapter		: our soft state
69162306a36Sopenharmony_ci *
69262306a36Sopenharmony_ci * - Allocate 16-byte aligned mailbox memory for firmware handshake
69362306a36Sopenharmony_ci * - Allocate controller's memory resources
69462306a36Sopenharmony_ci * - Find out all initialization data
69562306a36Sopenharmony_ci * - Allocate memory required for all the commands
69662306a36Sopenharmony_ci * - Use internal library of FW routines, build up complete soft state
69762306a36Sopenharmony_ci */
69862306a36Sopenharmony_cistatic int
69962306a36Sopenharmony_cimegaraid_init_mbox(adapter_t *adapter)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	struct pci_dev		*pdev;
70262306a36Sopenharmony_ci	mraid_device_t		*raid_dev;
70362306a36Sopenharmony_ci	int			i;
70462306a36Sopenharmony_ci	uint32_t		magic64;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	adapter->ito	= MBOX_TIMEOUT;
70862306a36Sopenharmony_ci	pdev		= adapter->pdev;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	/*
71162306a36Sopenharmony_ci	 * Allocate and initialize the init data structure for mailbox
71262306a36Sopenharmony_ci	 * controllers
71362306a36Sopenharmony_ci	 */
71462306a36Sopenharmony_ci	raid_dev = kzalloc(sizeof(mraid_device_t), GFP_KERNEL);
71562306a36Sopenharmony_ci	if (raid_dev == NULL) return -1;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/*
71962306a36Sopenharmony_ci	 * Attach the adapter soft state to raid device soft state
72062306a36Sopenharmony_ci	 */
72162306a36Sopenharmony_ci	adapter->raid_device	= (caddr_t)raid_dev;
72262306a36Sopenharmony_ci	raid_dev->fast_load	= megaraid_fast_load;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	// our baseport
72662306a36Sopenharmony_ci	raid_dev->baseport = pci_resource_start(pdev, 0);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	if (pci_request_regions(pdev, "MegaRAID: LSI Logic Corporation") != 0) {
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
73162306a36Sopenharmony_ci				"megaraid: mem region busy\n"));
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		goto out_free_raid_dev;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	raid_dev->baseaddr = ioremap(raid_dev->baseport, 128);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	if (!raid_dev->baseaddr) {
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
74162306a36Sopenharmony_ci			"megaraid: could not map hba memory\n") );
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci		goto out_release_regions;
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	/* initialize the mutual exclusion lock for the mailbox */
74762306a36Sopenharmony_ci	spin_lock_init(&raid_dev->mailbox_lock);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	/* allocate memory required for commands */
75062306a36Sopenharmony_ci	if (megaraid_alloc_cmd_packets(adapter) != 0)
75162306a36Sopenharmony_ci		goto out_iounmap;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	/*
75462306a36Sopenharmony_ci	 * Issue SYNC cmd to flush the pending cmds in the adapter
75562306a36Sopenharmony_ci	 * and initialize its internal state
75662306a36Sopenharmony_ci	 */
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (megaraid_mbox_fire_sync_cmd(adapter))
75962306a36Sopenharmony_ci		con_log(CL_ANN, ("megaraid: sync cmd failed\n"));
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	/*
76262306a36Sopenharmony_ci	 * Setup the rest of the soft state using the library of
76362306a36Sopenharmony_ci	 * FW routines
76462306a36Sopenharmony_ci	 */
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	/* request IRQ and register the interrupt service routine */
76762306a36Sopenharmony_ci	if (request_irq(adapter->irq, megaraid_isr, IRQF_SHARED, "megaraid",
76862306a36Sopenharmony_ci		adapter)) {
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
77162306a36Sopenharmony_ci			"megaraid: Couldn't register IRQ %d!\n", adapter->irq));
77262306a36Sopenharmony_ci		goto out_alloc_cmds;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	// Product info
77762306a36Sopenharmony_ci	if (megaraid_mbox_product_info(adapter) != 0)
77862306a36Sopenharmony_ci		goto out_free_irq;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	// Do we support extended CDBs
78162306a36Sopenharmony_ci	adapter->max_cdb_sz = 10;
78262306a36Sopenharmony_ci	if (megaraid_mbox_extended_cdb(adapter) == 0) {
78362306a36Sopenharmony_ci		adapter->max_cdb_sz = 16;
78462306a36Sopenharmony_ci	}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	/*
78762306a36Sopenharmony_ci	 * Do we support cluster environment, if we do, what is the initiator
78862306a36Sopenharmony_ci	 * id.
78962306a36Sopenharmony_ci	 * NOTE: In a non-cluster aware firmware environment, the LLD should
79062306a36Sopenharmony_ci	 * return 7 as initiator id.
79162306a36Sopenharmony_ci	 */
79262306a36Sopenharmony_ci	adapter->ha		= 0;
79362306a36Sopenharmony_ci	adapter->init_id	= -1;
79462306a36Sopenharmony_ci	if (megaraid_mbox_support_ha(adapter, &adapter->init_id) == 0) {
79562306a36Sopenharmony_ci		adapter->ha = 1;
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	/*
79962306a36Sopenharmony_ci	 * Prepare the device ids array to have the mapping between the kernel
80062306a36Sopenharmony_ci	 * device address and megaraid device address.
80162306a36Sopenharmony_ci	 * We export the physical devices on their actual addresses. The
80262306a36Sopenharmony_ci	 * logical drives are exported on a virtual SCSI channel
80362306a36Sopenharmony_ci	 */
80462306a36Sopenharmony_ci	megaraid_mbox_setup_device_map(adapter);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	// If the firmware supports random deletion, update the device id map
80762306a36Sopenharmony_ci	if (megaraid_mbox_support_random_del(adapter)) {
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci		// Change the logical drives numbers in device_ids array one
81062306a36Sopenharmony_ci		// slot in device_ids is reserved for target id, that's why
81162306a36Sopenharmony_ci		// "<=" below
81262306a36Sopenharmony_ci		for (i = 0; i <= MAX_LOGICAL_DRIVES_40LD; i++) {
81362306a36Sopenharmony_ci			adapter->device_ids[adapter->max_channel][i] += 0x80;
81462306a36Sopenharmony_ci		}
81562306a36Sopenharmony_ci		adapter->device_ids[adapter->max_channel][adapter->init_id] =
81662306a36Sopenharmony_ci			0xFF;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci		raid_dev->random_del_supported = 1;
81962306a36Sopenharmony_ci	}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	/*
82262306a36Sopenharmony_ci	 * find out the maximum number of scatter-gather elements supported by
82362306a36Sopenharmony_ci	 * this firmware
82462306a36Sopenharmony_ci	 */
82562306a36Sopenharmony_ci	adapter->sglen = megaraid_mbox_get_max_sg(adapter);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	// enumerate RAID and SCSI channels so that all devices on SCSI
82862306a36Sopenharmony_ci	// channels can later be exported, including disk devices
82962306a36Sopenharmony_ci	megaraid_mbox_enum_raid_scsi(adapter);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	/*
83262306a36Sopenharmony_ci	 * Other parameters required by upper layer
83362306a36Sopenharmony_ci	 *
83462306a36Sopenharmony_ci	 * maximum number of sectors per IO command
83562306a36Sopenharmony_ci	 */
83662306a36Sopenharmony_ci	adapter->max_sectors = megaraid_max_sectors;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	/*
83962306a36Sopenharmony_ci	 * number of queued commands per LUN.
84062306a36Sopenharmony_ci	 */
84162306a36Sopenharmony_ci	adapter->cmd_per_lun = megaraid_cmd_per_lun;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	/*
84462306a36Sopenharmony_ci	 * Allocate resources required to issue FW calls, when sysfs is
84562306a36Sopenharmony_ci	 * accessed
84662306a36Sopenharmony_ci	 */
84762306a36Sopenharmony_ci	if (megaraid_sysfs_alloc_resources(adapter) != 0)
84862306a36Sopenharmony_ci		goto out_free_irq;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	// Set the DMA mask to 64-bit. All supported controllers as capable of
85162306a36Sopenharmony_ci	// DMA in this range
85262306a36Sopenharmony_ci	pci_read_config_dword(adapter->pdev, PCI_CONF_AMISIG64, &magic64);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	if (((magic64 == HBA_SIGNATURE_64_BIT) &&
85562306a36Sopenharmony_ci		((adapter->pdev->subsystem_device !=
85662306a36Sopenharmony_ci		PCI_SUBSYS_ID_MEGARAID_SATA_150_6) &&
85762306a36Sopenharmony_ci		(adapter->pdev->subsystem_device !=
85862306a36Sopenharmony_ci		PCI_SUBSYS_ID_MEGARAID_SATA_150_4))) ||
85962306a36Sopenharmony_ci		(adapter->pdev->vendor == PCI_VENDOR_ID_LSI_LOGIC &&
86062306a36Sopenharmony_ci		adapter->pdev->device == PCI_DEVICE_ID_VERDE) ||
86162306a36Sopenharmony_ci		(adapter->pdev->vendor == PCI_VENDOR_ID_LSI_LOGIC &&
86262306a36Sopenharmony_ci		adapter->pdev->device == PCI_DEVICE_ID_DOBSON) ||
86362306a36Sopenharmony_ci		(adapter->pdev->vendor == PCI_VENDOR_ID_LSI_LOGIC &&
86462306a36Sopenharmony_ci		adapter->pdev->device == PCI_DEVICE_ID_LINDSAY) ||
86562306a36Sopenharmony_ci		(adapter->pdev->vendor == PCI_VENDOR_ID_DELL &&
86662306a36Sopenharmony_ci		adapter->pdev->device == PCI_DEVICE_ID_PERC4_DI_EVERGLADES) ||
86762306a36Sopenharmony_ci		(adapter->pdev->vendor == PCI_VENDOR_ID_DELL &&
86862306a36Sopenharmony_ci		adapter->pdev->device == PCI_DEVICE_ID_PERC4E_DI_KOBUK)) {
86962306a36Sopenharmony_ci		if (dma_set_mask(&adapter->pdev->dev, DMA_BIT_MASK(64))) {
87062306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_WARNING
87162306a36Sopenharmony_ci				"megaraid: DMA mask for 64-bit failed\n"));
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci			if (dma_set_mask(&adapter->pdev->dev,
87462306a36Sopenharmony_ci						DMA_BIT_MASK(32))) {
87562306a36Sopenharmony_ci				con_log(CL_ANN, (KERN_WARNING
87662306a36Sopenharmony_ci					"megaraid: 32-bit DMA mask failed\n"));
87762306a36Sopenharmony_ci				goto out_free_sysfs_res;
87862306a36Sopenharmony_ci			}
87962306a36Sopenharmony_ci		}
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	// setup tasklet for DPC
88362306a36Sopenharmony_ci	tasklet_init(&adapter->dpc_h, megaraid_mbox_dpc,
88462306a36Sopenharmony_ci			(unsigned long)adapter);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	con_log(CL_DLEVEL1, (KERN_INFO
88762306a36Sopenharmony_ci		"megaraid mbox hba successfully initialized\n"));
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	return 0;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ciout_free_sysfs_res:
89262306a36Sopenharmony_ci	megaraid_sysfs_free_resources(adapter);
89362306a36Sopenharmony_ciout_free_irq:
89462306a36Sopenharmony_ci	free_irq(adapter->irq, adapter);
89562306a36Sopenharmony_ciout_alloc_cmds:
89662306a36Sopenharmony_ci	megaraid_free_cmd_packets(adapter);
89762306a36Sopenharmony_ciout_iounmap:
89862306a36Sopenharmony_ci	iounmap(raid_dev->baseaddr);
89962306a36Sopenharmony_ciout_release_regions:
90062306a36Sopenharmony_ci	pci_release_regions(pdev);
90162306a36Sopenharmony_ciout_free_raid_dev:
90262306a36Sopenharmony_ci	kfree(raid_dev);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	return -1;
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci/**
90962306a36Sopenharmony_ci * megaraid_fini_mbox - undo controller initialization
91062306a36Sopenharmony_ci * @adapter		: our soft state
91162306a36Sopenharmony_ci */
91262306a36Sopenharmony_cistatic void
91362306a36Sopenharmony_cimegaraid_fini_mbox(adapter_t *adapter)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	// flush all caches
91862306a36Sopenharmony_ci	megaraid_mbox_flush_cache(adapter);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	tasklet_kill(&adapter->dpc_h);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	megaraid_sysfs_free_resources(adapter);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	megaraid_free_cmd_packets(adapter);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	free_irq(adapter->irq, adapter);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	iounmap(raid_dev->baseaddr);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	pci_release_regions(adapter->pdev);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	kfree(raid_dev);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	return;
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci/**
93962306a36Sopenharmony_ci * megaraid_alloc_cmd_packets - allocate shared mailbox
94062306a36Sopenharmony_ci * @adapter		: soft state of the raid controller
94162306a36Sopenharmony_ci *
94262306a36Sopenharmony_ci * Allocate and align the shared mailbox. This mailbox is used to issue
94362306a36Sopenharmony_ci * all the commands. For IO based controllers, the mailbox is also registered
94462306a36Sopenharmony_ci * with the FW. Allocate memory for all commands as well.
94562306a36Sopenharmony_ci * This is our big allocator.
94662306a36Sopenharmony_ci */
94762306a36Sopenharmony_cistatic int
94862306a36Sopenharmony_cimegaraid_alloc_cmd_packets(adapter_t *adapter)
94962306a36Sopenharmony_ci{
95062306a36Sopenharmony_ci	mraid_device_t		*raid_dev = ADAP2RAIDDEV(adapter);
95162306a36Sopenharmony_ci	struct pci_dev		*pdev;
95262306a36Sopenharmony_ci	unsigned long		align;
95362306a36Sopenharmony_ci	scb_t			*scb;
95462306a36Sopenharmony_ci	mbox_ccb_t		*ccb;
95562306a36Sopenharmony_ci	struct mraid_pci_blk	*epthru_pci_blk;
95662306a36Sopenharmony_ci	struct mraid_pci_blk	*sg_pci_blk;
95762306a36Sopenharmony_ci	struct mraid_pci_blk	*mbox_pci_blk;
95862306a36Sopenharmony_ci	int			i;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	pdev = adapter->pdev;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	/*
96362306a36Sopenharmony_ci	 * Setup the mailbox
96462306a36Sopenharmony_ci	 * Allocate the common 16-byte aligned memory for the handshake
96562306a36Sopenharmony_ci	 * mailbox.
96662306a36Sopenharmony_ci	 */
96762306a36Sopenharmony_ci	raid_dev->una_mbox64 = dma_alloc_coherent(&adapter->pdev->dev,
96862306a36Sopenharmony_ci						  sizeof(mbox64_t),
96962306a36Sopenharmony_ci						  &raid_dev->una_mbox64_dma,
97062306a36Sopenharmony_ci						  GFP_KERNEL);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	if (!raid_dev->una_mbox64) {
97362306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
97462306a36Sopenharmony_ci			"megaraid: out of memory, %s %d\n", __func__,
97562306a36Sopenharmony_ci			__LINE__));
97662306a36Sopenharmony_ci		return -1;
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	/*
98062306a36Sopenharmony_ci	 * Align the mailbox at 16-byte boundary
98162306a36Sopenharmony_ci	 */
98262306a36Sopenharmony_ci	raid_dev->mbox	= &raid_dev->una_mbox64->mbox32;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	raid_dev->mbox	= (mbox_t *)((((unsigned long)raid_dev->mbox) + 15) &
98562306a36Sopenharmony_ci				(~0UL ^ 0xFUL));
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	raid_dev->mbox64 = (mbox64_t *)(((unsigned long)raid_dev->mbox) - 8);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	align = ((void *)raid_dev->mbox -
99062306a36Sopenharmony_ci			((void *)&raid_dev->una_mbox64->mbox32));
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	raid_dev->mbox_dma = (unsigned long)raid_dev->una_mbox64_dma + 8 +
99362306a36Sopenharmony_ci			align;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	// Allocate memory for commands issued internally
99662306a36Sopenharmony_ci	adapter->ibuf = dma_alloc_coherent(&pdev->dev, MBOX_IBUF_SIZE,
99762306a36Sopenharmony_ci					   &adapter->ibuf_dma_h, GFP_KERNEL);
99862306a36Sopenharmony_ci	if (!adapter->ibuf) {
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
100162306a36Sopenharmony_ci			"megaraid: out of memory, %s %d\n", __func__,
100262306a36Sopenharmony_ci			__LINE__));
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci		goto out_free_common_mbox;
100562306a36Sopenharmony_ci	}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	// Allocate memory for our SCSI Command Blocks and their associated
100862306a36Sopenharmony_ci	// memory
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	/*
101162306a36Sopenharmony_ci	 * Allocate memory for the base list of scb. Later allocate memory for
101262306a36Sopenharmony_ci	 * CCBs and embedded components of each CCB and point the pointers in
101362306a36Sopenharmony_ci	 * scb to the allocated components
101462306a36Sopenharmony_ci	 * NOTE: The code to allocate SCB will be duplicated in all the LLD
101562306a36Sopenharmony_ci	 * since the calling routine does not yet know the number of available
101662306a36Sopenharmony_ci	 * commands.
101762306a36Sopenharmony_ci	 */
101862306a36Sopenharmony_ci	adapter->kscb_list = kcalloc(MBOX_MAX_SCSI_CMDS, sizeof(scb_t), GFP_KERNEL);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	if (adapter->kscb_list == NULL) {
102162306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
102262306a36Sopenharmony_ci			"megaraid: out of memory, %s %d\n", __func__,
102362306a36Sopenharmony_ci			__LINE__));
102462306a36Sopenharmony_ci		goto out_free_ibuf;
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	// memory allocation for our command packets
102862306a36Sopenharmony_ci	if (megaraid_mbox_setup_dma_pools(adapter) != 0) {
102962306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
103062306a36Sopenharmony_ci			"megaraid: out of memory, %s %d\n", __func__,
103162306a36Sopenharmony_ci			__LINE__));
103262306a36Sopenharmony_ci		goto out_free_scb_list;
103362306a36Sopenharmony_ci	}
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	// Adjust the scb pointers and link in the free pool
103662306a36Sopenharmony_ci	epthru_pci_blk	= raid_dev->epthru_pool;
103762306a36Sopenharmony_ci	sg_pci_blk	= raid_dev->sg_pool;
103862306a36Sopenharmony_ci	mbox_pci_blk	= raid_dev->mbox_pool;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
104162306a36Sopenharmony_ci		scb			= adapter->kscb_list + i;
104262306a36Sopenharmony_ci		ccb			= raid_dev->ccb_list + i;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci		ccb->mbox	= (mbox_t *)(mbox_pci_blk[i].vaddr + 16);
104562306a36Sopenharmony_ci		ccb->raw_mbox	= (uint8_t *)ccb->mbox;
104662306a36Sopenharmony_ci		ccb->mbox64	= (mbox64_t *)(mbox_pci_blk[i].vaddr + 8);
104762306a36Sopenharmony_ci		ccb->mbox_dma_h	= (unsigned long)mbox_pci_blk[i].dma_addr + 16;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci		// make sure the mailbox is aligned properly
105062306a36Sopenharmony_ci		if (ccb->mbox_dma_h & 0x0F) {
105162306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_CRIT
105262306a36Sopenharmony_ci				"megaraid mbox: not aligned on 16-bytes\n"));
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci			goto out_teardown_dma_pools;
105562306a36Sopenharmony_ci		}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci		ccb->epthru		= (mraid_epassthru_t *)
105862306a36Sopenharmony_ci						epthru_pci_blk[i].vaddr;
105962306a36Sopenharmony_ci		ccb->epthru_dma_h	= epthru_pci_blk[i].dma_addr;
106062306a36Sopenharmony_ci		ccb->pthru		= (mraid_passthru_t *)ccb->epthru;
106162306a36Sopenharmony_ci		ccb->pthru_dma_h	= ccb->epthru_dma_h;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci		ccb->sgl64		= (mbox_sgl64 *)sg_pci_blk[i].vaddr;
106562306a36Sopenharmony_ci		ccb->sgl_dma_h		= sg_pci_blk[i].dma_addr;
106662306a36Sopenharmony_ci		ccb->sgl32		= (mbox_sgl32 *)ccb->sgl64;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci		scb->ccb		= (caddr_t)ccb;
106962306a36Sopenharmony_ci		scb->gp			= 0;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci		scb->sno		= i;	// command index
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci		scb->scp		= NULL;
107462306a36Sopenharmony_ci		scb->state		= SCB_FREE;
107562306a36Sopenharmony_ci		scb->dma_direction	= DMA_NONE;
107662306a36Sopenharmony_ci		scb->dma_type		= MRAID_DMA_NONE;
107762306a36Sopenharmony_ci		scb->dev_channel	= -1;
107862306a36Sopenharmony_ci		scb->dev_target		= -1;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci		// put scb in the free pool
108162306a36Sopenharmony_ci		list_add_tail(&scb->list, &adapter->kscb_pool);
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	return 0;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ciout_teardown_dma_pools:
108762306a36Sopenharmony_ci	megaraid_mbox_teardown_dma_pools(adapter);
108862306a36Sopenharmony_ciout_free_scb_list:
108962306a36Sopenharmony_ci	kfree(adapter->kscb_list);
109062306a36Sopenharmony_ciout_free_ibuf:
109162306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, MBOX_IBUF_SIZE, (void *)adapter->ibuf,
109262306a36Sopenharmony_ci		adapter->ibuf_dma_h);
109362306a36Sopenharmony_ciout_free_common_mbox:
109462306a36Sopenharmony_ci	dma_free_coherent(&adapter->pdev->dev, sizeof(mbox64_t),
109562306a36Sopenharmony_ci		(caddr_t)raid_dev->una_mbox64, raid_dev->una_mbox64_dma);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	return -1;
109862306a36Sopenharmony_ci}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci/**
110262306a36Sopenharmony_ci * megaraid_free_cmd_packets - free memory
110362306a36Sopenharmony_ci * @adapter		: soft state of the raid controller
110462306a36Sopenharmony_ci *
110562306a36Sopenharmony_ci * Release memory resources allocated for commands.
110662306a36Sopenharmony_ci */
110762306a36Sopenharmony_cistatic void
110862306a36Sopenharmony_cimegaraid_free_cmd_packets(adapter_t *adapter)
110962306a36Sopenharmony_ci{
111062306a36Sopenharmony_ci	mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	megaraid_mbox_teardown_dma_pools(adapter);
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	kfree(adapter->kscb_list);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	dma_free_coherent(&adapter->pdev->dev, MBOX_IBUF_SIZE,
111762306a36Sopenharmony_ci		(void *)adapter->ibuf, adapter->ibuf_dma_h);
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	dma_free_coherent(&adapter->pdev->dev, sizeof(mbox64_t),
112062306a36Sopenharmony_ci		(caddr_t)raid_dev->una_mbox64, raid_dev->una_mbox64_dma);
112162306a36Sopenharmony_ci	return;
112262306a36Sopenharmony_ci}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci/**
112662306a36Sopenharmony_ci * megaraid_mbox_setup_dma_pools - setup dma pool for command packets
112762306a36Sopenharmony_ci * @adapter		: HBA soft state
112862306a36Sopenharmony_ci *
112962306a36Sopenharmony_ci * Setup the dma pools for mailbox, passthru and extended passthru structures,
113062306a36Sopenharmony_ci * and scatter-gather lists.
113162306a36Sopenharmony_ci */
113262306a36Sopenharmony_cistatic int
113362306a36Sopenharmony_cimegaraid_mbox_setup_dma_pools(adapter_t *adapter)
113462306a36Sopenharmony_ci{
113562306a36Sopenharmony_ci	mraid_device_t		*raid_dev = ADAP2RAIDDEV(adapter);
113662306a36Sopenharmony_ci	struct mraid_pci_blk	*epthru_pci_blk;
113762306a36Sopenharmony_ci	struct mraid_pci_blk	*sg_pci_blk;
113862306a36Sopenharmony_ci	struct mraid_pci_blk	*mbox_pci_blk;
113962306a36Sopenharmony_ci	int			i;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	// Allocate memory for 16-bytes aligned mailboxes
114462306a36Sopenharmony_ci	raid_dev->mbox_pool_handle = dma_pool_create("megaraid mbox pool",
114562306a36Sopenharmony_ci						&adapter->pdev->dev,
114662306a36Sopenharmony_ci						sizeof(mbox64_t) + 16,
114762306a36Sopenharmony_ci						16, 0);
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	if (raid_dev->mbox_pool_handle == NULL) {
115062306a36Sopenharmony_ci		goto fail_setup_dma_pool;
115162306a36Sopenharmony_ci	}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	mbox_pci_blk = raid_dev->mbox_pool;
115462306a36Sopenharmony_ci	for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
115562306a36Sopenharmony_ci		mbox_pci_blk[i].vaddr = dma_pool_alloc(
115662306a36Sopenharmony_ci						raid_dev->mbox_pool_handle,
115762306a36Sopenharmony_ci						GFP_KERNEL,
115862306a36Sopenharmony_ci						&mbox_pci_blk[i].dma_addr);
115962306a36Sopenharmony_ci		if (!mbox_pci_blk[i].vaddr) {
116062306a36Sopenharmony_ci			goto fail_setup_dma_pool;
116162306a36Sopenharmony_ci		}
116262306a36Sopenharmony_ci	}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	/*
116562306a36Sopenharmony_ci	 * Allocate memory for each embedded passthru strucuture pointer
116662306a36Sopenharmony_ci	 * Request for a 128 bytes aligned structure for each passthru command
116762306a36Sopenharmony_ci	 * structure
116862306a36Sopenharmony_ci	 * Since passthru and extended passthru commands are exclusive, they
116962306a36Sopenharmony_ci	 * share common memory pool. Passthru structures piggyback on memory
117062306a36Sopenharmony_ci	 * allocated to extended passthru since passthru is smaller of the two
117162306a36Sopenharmony_ci	 */
117262306a36Sopenharmony_ci	raid_dev->epthru_pool_handle = dma_pool_create("megaraid mbox pthru",
117362306a36Sopenharmony_ci			&adapter->pdev->dev, sizeof(mraid_epassthru_t), 128, 0);
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	if (raid_dev->epthru_pool_handle == NULL) {
117662306a36Sopenharmony_ci		goto fail_setup_dma_pool;
117762306a36Sopenharmony_ci	}
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	epthru_pci_blk = raid_dev->epthru_pool;
118062306a36Sopenharmony_ci	for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
118162306a36Sopenharmony_ci		epthru_pci_blk[i].vaddr = dma_pool_alloc(
118262306a36Sopenharmony_ci						raid_dev->epthru_pool_handle,
118362306a36Sopenharmony_ci						GFP_KERNEL,
118462306a36Sopenharmony_ci						&epthru_pci_blk[i].dma_addr);
118562306a36Sopenharmony_ci		if (!epthru_pci_blk[i].vaddr) {
118662306a36Sopenharmony_ci			goto fail_setup_dma_pool;
118762306a36Sopenharmony_ci		}
118862306a36Sopenharmony_ci	}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	// Allocate memory for each scatter-gather list. Request for 512 bytes
119262306a36Sopenharmony_ci	// alignment for each sg list
119362306a36Sopenharmony_ci	raid_dev->sg_pool_handle = dma_pool_create("megaraid mbox sg",
119462306a36Sopenharmony_ci					&adapter->pdev->dev,
119562306a36Sopenharmony_ci					sizeof(mbox_sgl64) * MBOX_MAX_SG_SIZE,
119662306a36Sopenharmony_ci					512, 0);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	if (raid_dev->sg_pool_handle == NULL) {
119962306a36Sopenharmony_ci		goto fail_setup_dma_pool;
120062306a36Sopenharmony_ci	}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	sg_pci_blk = raid_dev->sg_pool;
120362306a36Sopenharmony_ci	for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
120462306a36Sopenharmony_ci		sg_pci_blk[i].vaddr = dma_pool_alloc(
120562306a36Sopenharmony_ci						raid_dev->sg_pool_handle,
120662306a36Sopenharmony_ci						GFP_KERNEL,
120762306a36Sopenharmony_ci						&sg_pci_blk[i].dma_addr);
120862306a36Sopenharmony_ci		if (!sg_pci_blk[i].vaddr) {
120962306a36Sopenharmony_ci			goto fail_setup_dma_pool;
121062306a36Sopenharmony_ci		}
121162306a36Sopenharmony_ci	}
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	return 0;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_cifail_setup_dma_pool:
121662306a36Sopenharmony_ci	megaraid_mbox_teardown_dma_pools(adapter);
121762306a36Sopenharmony_ci	return -1;
121862306a36Sopenharmony_ci}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci/**
122262306a36Sopenharmony_ci * megaraid_mbox_teardown_dma_pools - teardown dma pools for command packets
122362306a36Sopenharmony_ci * @adapter		: HBA soft state
122462306a36Sopenharmony_ci *
122562306a36Sopenharmony_ci * Teardown the dma pool for mailbox, passthru and extended passthru
122662306a36Sopenharmony_ci * structures, and scatter-gather lists.
122762306a36Sopenharmony_ci */
122862306a36Sopenharmony_cistatic void
122962306a36Sopenharmony_cimegaraid_mbox_teardown_dma_pools(adapter_t *adapter)
123062306a36Sopenharmony_ci{
123162306a36Sopenharmony_ci	mraid_device_t		*raid_dev = ADAP2RAIDDEV(adapter);
123262306a36Sopenharmony_ci	struct mraid_pci_blk	*epthru_pci_blk;
123362306a36Sopenharmony_ci	struct mraid_pci_blk	*sg_pci_blk;
123462306a36Sopenharmony_ci	struct mraid_pci_blk	*mbox_pci_blk;
123562306a36Sopenharmony_ci	int			i;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	sg_pci_blk = raid_dev->sg_pool;
123962306a36Sopenharmony_ci	for (i = 0; i < MBOX_MAX_SCSI_CMDS && sg_pci_blk[i].vaddr; i++) {
124062306a36Sopenharmony_ci		dma_pool_free(raid_dev->sg_pool_handle, sg_pci_blk[i].vaddr,
124162306a36Sopenharmony_ci			sg_pci_blk[i].dma_addr);
124262306a36Sopenharmony_ci	}
124362306a36Sopenharmony_ci	dma_pool_destroy(raid_dev->sg_pool_handle);
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	epthru_pci_blk = raid_dev->epthru_pool;
124762306a36Sopenharmony_ci	for (i = 0; i < MBOX_MAX_SCSI_CMDS && epthru_pci_blk[i].vaddr; i++) {
124862306a36Sopenharmony_ci		dma_pool_free(raid_dev->epthru_pool_handle,
124962306a36Sopenharmony_ci			epthru_pci_blk[i].vaddr, epthru_pci_blk[i].dma_addr);
125062306a36Sopenharmony_ci	}
125162306a36Sopenharmony_ci	dma_pool_destroy(raid_dev->epthru_pool_handle);
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	mbox_pci_blk = raid_dev->mbox_pool;
125562306a36Sopenharmony_ci	for (i = 0; i < MBOX_MAX_SCSI_CMDS && mbox_pci_blk[i].vaddr; i++) {
125662306a36Sopenharmony_ci		dma_pool_free(raid_dev->mbox_pool_handle,
125762306a36Sopenharmony_ci			mbox_pci_blk[i].vaddr, mbox_pci_blk[i].dma_addr);
125862306a36Sopenharmony_ci	}
125962306a36Sopenharmony_ci	dma_pool_destroy(raid_dev->mbox_pool_handle);
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	return;
126262306a36Sopenharmony_ci}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci/**
126662306a36Sopenharmony_ci * megaraid_alloc_scb - detach and return a scb from the free list
126762306a36Sopenharmony_ci * @adapter	: controller's soft state
126862306a36Sopenharmony_ci * @scp		: pointer to the scsi command to be executed
126962306a36Sopenharmony_ci *
127062306a36Sopenharmony_ci * Return the scb from the head of the free list. %NULL if there are none
127162306a36Sopenharmony_ci * available.
127262306a36Sopenharmony_ci */
127362306a36Sopenharmony_cistatic scb_t *
127462306a36Sopenharmony_cimegaraid_alloc_scb(adapter_t *adapter, struct scsi_cmnd *scp)
127562306a36Sopenharmony_ci{
127662306a36Sopenharmony_ci	struct list_head	*head = &adapter->kscb_pool;
127762306a36Sopenharmony_ci	scb_t			*scb = NULL;
127862306a36Sopenharmony_ci	unsigned long		flags;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	// detach scb from free pool
128162306a36Sopenharmony_ci	spin_lock_irqsave(SCSI_FREE_LIST_LOCK(adapter), flags);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	if (list_empty(head)) {
128462306a36Sopenharmony_ci		spin_unlock_irqrestore(SCSI_FREE_LIST_LOCK(adapter), flags);
128562306a36Sopenharmony_ci		return NULL;
128662306a36Sopenharmony_ci	}
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	scb = list_entry(head->next, scb_t, list);
128962306a36Sopenharmony_ci	list_del_init(&scb->list);
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	spin_unlock_irqrestore(SCSI_FREE_LIST_LOCK(adapter), flags);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	scb->state	= SCB_ACTIVE;
129462306a36Sopenharmony_ci	scb->scp	= scp;
129562306a36Sopenharmony_ci	scb->dma_type	= MRAID_DMA_NONE;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	return scb;
129862306a36Sopenharmony_ci}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci/**
130262306a36Sopenharmony_ci * megaraid_dealloc_scb - return the scb to the free pool
130362306a36Sopenharmony_ci * @adapter	: controller's soft state
130462306a36Sopenharmony_ci * @scb		: scb to be freed
130562306a36Sopenharmony_ci *
130662306a36Sopenharmony_ci * Return the scb back to the free list of scbs. The caller must 'flush' the
130762306a36Sopenharmony_ci * SCB before calling us. E.g., performing pci_unamp and/or pci_sync etc.
130862306a36Sopenharmony_ci * NOTE NOTE: Make sure the scb is not on any list before calling this
130962306a36Sopenharmony_ci * routine.
131062306a36Sopenharmony_ci */
131162306a36Sopenharmony_cistatic inline void
131262306a36Sopenharmony_cimegaraid_dealloc_scb(adapter_t *adapter, scb_t *scb)
131362306a36Sopenharmony_ci{
131462306a36Sopenharmony_ci	unsigned long		flags;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	// put scb in the free pool
131762306a36Sopenharmony_ci	scb->state	= SCB_FREE;
131862306a36Sopenharmony_ci	scb->scp	= NULL;
131962306a36Sopenharmony_ci	spin_lock_irqsave(SCSI_FREE_LIST_LOCK(adapter), flags);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	list_add(&scb->list, &adapter->kscb_pool);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	spin_unlock_irqrestore(SCSI_FREE_LIST_LOCK(adapter), flags);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	return;
132662306a36Sopenharmony_ci}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci/**
133062306a36Sopenharmony_ci * megaraid_mbox_mksgl - make the scatter-gather list
133162306a36Sopenharmony_ci * @adapter	: controller's soft state
133262306a36Sopenharmony_ci * @scb		: scsi control block
133362306a36Sopenharmony_ci *
133462306a36Sopenharmony_ci * Prepare the scatter-gather list.
133562306a36Sopenharmony_ci */
133662306a36Sopenharmony_cistatic int
133762306a36Sopenharmony_cimegaraid_mbox_mksgl(adapter_t *adapter, scb_t *scb)
133862306a36Sopenharmony_ci{
133962306a36Sopenharmony_ci	struct scatterlist	*sgl;
134062306a36Sopenharmony_ci	mbox_ccb_t		*ccb;
134162306a36Sopenharmony_ci	struct scsi_cmnd	*scp;
134262306a36Sopenharmony_ci	int			sgcnt;
134362306a36Sopenharmony_ci	int			i;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	scp	= scb->scp;
134762306a36Sopenharmony_ci	ccb	= (mbox_ccb_t *)scb->ccb;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	sgcnt = scsi_dma_map(scp);
135062306a36Sopenharmony_ci	BUG_ON(sgcnt < 0 || sgcnt > adapter->sglen);
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	// no mapping required if no data to be transferred
135362306a36Sopenharmony_ci	if (!sgcnt)
135462306a36Sopenharmony_ci		return 0;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	scb->dma_type = MRAID_DMA_WSG;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	scsi_for_each_sg(scp, sgl, sgcnt, i) {
135962306a36Sopenharmony_ci		ccb->sgl64[i].address	= sg_dma_address(sgl);
136062306a36Sopenharmony_ci		ccb->sgl64[i].length	= sg_dma_len(sgl);
136162306a36Sopenharmony_ci	}
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	// Return count of SG nodes
136462306a36Sopenharmony_ci	return sgcnt;
136562306a36Sopenharmony_ci}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci/**
136962306a36Sopenharmony_ci * mbox_post_cmd - issue a mailbox command
137062306a36Sopenharmony_ci * @adapter	: controller's soft state
137162306a36Sopenharmony_ci * @scb		: command to be issued
137262306a36Sopenharmony_ci *
137362306a36Sopenharmony_ci * Post the command to the controller if mailbox is available.
137462306a36Sopenharmony_ci */
137562306a36Sopenharmony_cistatic int
137662306a36Sopenharmony_cimbox_post_cmd(adapter_t *adapter, scb_t *scb)
137762306a36Sopenharmony_ci{
137862306a36Sopenharmony_ci	mraid_device_t	*raid_dev = ADAP2RAIDDEV(adapter);
137962306a36Sopenharmony_ci	mbox64_t	*mbox64;
138062306a36Sopenharmony_ci	mbox_t		*mbox;
138162306a36Sopenharmony_ci	mbox_ccb_t	*ccb;
138262306a36Sopenharmony_ci	unsigned long	flags;
138362306a36Sopenharmony_ci	unsigned int	i = 0;
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	ccb	= (mbox_ccb_t *)scb->ccb;
138762306a36Sopenharmony_ci	mbox	= raid_dev->mbox;
138862306a36Sopenharmony_ci	mbox64	= raid_dev->mbox64;
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	/*
139162306a36Sopenharmony_ci	 * Check for busy mailbox. If it is, return failure - the caller
139262306a36Sopenharmony_ci	 * should retry later.
139362306a36Sopenharmony_ci	 */
139462306a36Sopenharmony_ci	spin_lock_irqsave(MAILBOX_LOCK(raid_dev), flags);
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	if (unlikely(mbox->busy)) {
139762306a36Sopenharmony_ci		do {
139862306a36Sopenharmony_ci			udelay(1);
139962306a36Sopenharmony_ci			i++;
140062306a36Sopenharmony_ci			rmb();
140162306a36Sopenharmony_ci		} while(mbox->busy && (i < max_mbox_busy_wait));
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci		if (mbox->busy) {
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci			spin_unlock_irqrestore(MAILBOX_LOCK(raid_dev), flags);
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci			return -1;
140862306a36Sopenharmony_ci		}
140962306a36Sopenharmony_ci	}
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	// Copy this command's mailbox data into "adapter's" mailbox
141362306a36Sopenharmony_ci	memcpy((caddr_t)mbox64, (caddr_t)ccb->mbox64, 22);
141462306a36Sopenharmony_ci	mbox->cmdid = scb->sno;
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	adapter->outstanding_cmds++;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	mbox->busy	= 1;	// Set busy
141962306a36Sopenharmony_ci	mbox->poll	= 0;
142062306a36Sopenharmony_ci	mbox->ack	= 0;
142162306a36Sopenharmony_ci	wmb();
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1);
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	spin_unlock_irqrestore(MAILBOX_LOCK(raid_dev), flags);
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	return 0;
142862306a36Sopenharmony_ci}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci/**
143262306a36Sopenharmony_ci * megaraid_queue_command_lck - generic queue entry point for all LLDs
143362306a36Sopenharmony_ci * @scp		: pointer to the scsi command to be executed
143462306a36Sopenharmony_ci *
143562306a36Sopenharmony_ci * Queue entry point for mailbox based controllers.
143662306a36Sopenharmony_ci */
143762306a36Sopenharmony_cistatic int megaraid_queue_command_lck(struct scsi_cmnd *scp)
143862306a36Sopenharmony_ci{
143962306a36Sopenharmony_ci	void (*done)(struct scsi_cmnd *) = scsi_done;
144062306a36Sopenharmony_ci	adapter_t	*adapter;
144162306a36Sopenharmony_ci	scb_t		*scb;
144262306a36Sopenharmony_ci	int		if_busy;
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	adapter		= SCP2ADAPTER(scp);
144562306a36Sopenharmony_ci	scp->result	= 0;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	/*
144862306a36Sopenharmony_ci	 * Allocate and build a SCB request
144962306a36Sopenharmony_ci	 * if_busy flag will be set if megaraid_mbox_build_cmd() command could
145062306a36Sopenharmony_ci	 * not allocate scb. We will return non-zero status in that case.
145162306a36Sopenharmony_ci	 * NOTE: scb can be null even though certain commands completed
145262306a36Sopenharmony_ci	 * successfully, e.g., MODE_SENSE and TEST_UNIT_READY, it would
145362306a36Sopenharmony_ci	 * return 0 in that case, and we would do the callback right away.
145462306a36Sopenharmony_ci	 */
145562306a36Sopenharmony_ci	if_busy	= 0;
145662306a36Sopenharmony_ci	scb = megaraid_mbox_build_cmd(adapter, scp, &if_busy);
145762306a36Sopenharmony_ci	if (!scb) {	// command already completed
145862306a36Sopenharmony_ci		done(scp);
145962306a36Sopenharmony_ci		return 0;
146062306a36Sopenharmony_ci	}
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	megaraid_mbox_runpendq(adapter, scb);
146362306a36Sopenharmony_ci	return if_busy;
146462306a36Sopenharmony_ci}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_cistatic DEF_SCSI_QCMD(megaraid_queue_command)
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci/**
146962306a36Sopenharmony_ci * megaraid_mbox_build_cmd - transform the mid-layer scsi commands
147062306a36Sopenharmony_ci * @adapter	: controller's soft state
147162306a36Sopenharmony_ci * @scp		: mid-layer scsi command pointer
147262306a36Sopenharmony_ci * @busy	: set if request could not be completed because of lack of
147362306a36Sopenharmony_ci *		resources
147462306a36Sopenharmony_ci *
147562306a36Sopenharmony_ci * Transform the mid-layer scsi command to megaraid firmware lingua.
147662306a36Sopenharmony_ci * Convert the command issued by mid-layer to format understood by megaraid
147762306a36Sopenharmony_ci * firmware. We also complete certain commands without sending them to firmware.
147862306a36Sopenharmony_ci */
147962306a36Sopenharmony_cistatic scb_t *
148062306a36Sopenharmony_cimegaraid_mbox_build_cmd(adapter_t *adapter, struct scsi_cmnd *scp, int *busy)
148162306a36Sopenharmony_ci{
148262306a36Sopenharmony_ci	mraid_device_t		*rdev = ADAP2RAIDDEV(adapter);
148362306a36Sopenharmony_ci	int			channel;
148462306a36Sopenharmony_ci	int			target;
148562306a36Sopenharmony_ci	int			islogical;
148662306a36Sopenharmony_ci	mbox_ccb_t		*ccb;
148762306a36Sopenharmony_ci	mraid_passthru_t	*pthru;
148862306a36Sopenharmony_ci	mbox64_t		*mbox64;
148962306a36Sopenharmony_ci	mbox_t			*mbox;
149062306a36Sopenharmony_ci	scb_t			*scb;
149162306a36Sopenharmony_ci	char			skip[] = "skipping";
149262306a36Sopenharmony_ci	char			scan[] = "scanning";
149362306a36Sopenharmony_ci	char			*ss;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	/*
149762306a36Sopenharmony_ci	 * Get the appropriate device map for the device this command is
149862306a36Sopenharmony_ci	 * intended for
149962306a36Sopenharmony_ci	 */
150062306a36Sopenharmony_ci	MRAID_GET_DEVICE_MAP(adapter, scp, channel, target, islogical);
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	/*
150362306a36Sopenharmony_ci	 * Logical drive commands
150462306a36Sopenharmony_ci	 */
150562306a36Sopenharmony_ci	if (islogical) {
150662306a36Sopenharmony_ci		switch (scp->cmnd[0]) {
150762306a36Sopenharmony_ci		case TEST_UNIT_READY:
150862306a36Sopenharmony_ci			/*
150962306a36Sopenharmony_ci			 * Do we support clustering and is the support enabled
151062306a36Sopenharmony_ci			 * If no, return success always
151162306a36Sopenharmony_ci			 */
151262306a36Sopenharmony_ci			if (!adapter->ha) {
151362306a36Sopenharmony_ci				scp->result = (DID_OK << 16);
151462306a36Sopenharmony_ci				return NULL;
151562306a36Sopenharmony_ci			}
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci			if (!(scb = megaraid_alloc_scb(adapter, scp))) {
151862306a36Sopenharmony_ci				scp->result = (DID_ERROR << 16);
151962306a36Sopenharmony_ci				*busy = 1;
152062306a36Sopenharmony_ci				return NULL;
152162306a36Sopenharmony_ci			}
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci			scb->dma_direction	= scp->sc_data_direction;
152462306a36Sopenharmony_ci			scb->dev_channel	= 0xFF;
152562306a36Sopenharmony_ci			scb->dev_target		= target;
152662306a36Sopenharmony_ci			ccb			= (mbox_ccb_t *)scb->ccb;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci			/*
152962306a36Sopenharmony_ci			 * The command id will be provided by the command
153062306a36Sopenharmony_ci			 * issuance routine
153162306a36Sopenharmony_ci			 */
153262306a36Sopenharmony_ci			ccb->raw_mbox[0]	= CLUSTER_CMD;
153362306a36Sopenharmony_ci			ccb->raw_mbox[2]	= RESERVATION_STATUS;
153462306a36Sopenharmony_ci			ccb->raw_mbox[3]	= target;
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci			return scb;
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci		case MODE_SENSE:
153962306a36Sopenharmony_ci		{
154062306a36Sopenharmony_ci			struct scatterlist	*sgl;
154162306a36Sopenharmony_ci			caddr_t			vaddr;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci			sgl = scsi_sglist(scp);
154462306a36Sopenharmony_ci			if (sg_page(sgl)) {
154562306a36Sopenharmony_ci				vaddr = (caddr_t) sg_virt(&sgl[0]);
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci				memset(vaddr, 0, scp->cmnd[4]);
154862306a36Sopenharmony_ci			}
154962306a36Sopenharmony_ci			else {
155062306a36Sopenharmony_ci				con_log(CL_ANN, (KERN_WARNING
155162306a36Sopenharmony_ci						 "megaraid mailbox: invalid sg:%d\n",
155262306a36Sopenharmony_ci						 __LINE__));
155362306a36Sopenharmony_ci			}
155462306a36Sopenharmony_ci		}
155562306a36Sopenharmony_ci		scp->result = (DID_OK << 16);
155662306a36Sopenharmony_ci		return NULL;
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci		case INQUIRY:
155962306a36Sopenharmony_ci			/*
156062306a36Sopenharmony_ci			 * Display the channel scan for logical drives
156162306a36Sopenharmony_ci			 * Do not display scan for a channel if already done.
156262306a36Sopenharmony_ci			 */
156362306a36Sopenharmony_ci			if (!(rdev->last_disp & (1L << SCP2CHANNEL(scp)))) {
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci				con_log(CL_ANN, (KERN_INFO
156662306a36Sopenharmony_ci					"scsi[%d]: scanning scsi channel %d",
156762306a36Sopenharmony_ci					adapter->host->host_no,
156862306a36Sopenharmony_ci					SCP2CHANNEL(scp)));
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci				con_log(CL_ANN, (
157162306a36Sopenharmony_ci					" [virtual] for logical drives\n"));
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci				rdev->last_disp |= (1L << SCP2CHANNEL(scp));
157462306a36Sopenharmony_ci			}
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci			if (scp->cmnd[1] & MEGA_SCSI_INQ_EVPD) {
157762306a36Sopenharmony_ci				scsi_build_sense(scp, 0, ILLEGAL_REQUEST,
157862306a36Sopenharmony_ci						 MEGA_INVALID_FIELD_IN_CDB, 0);
157962306a36Sopenharmony_ci				return NULL;
158062306a36Sopenharmony_ci			}
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci			fallthrough;
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci		case READ_CAPACITY:
158562306a36Sopenharmony_ci			/*
158662306a36Sopenharmony_ci			 * Do not allow LUN > 0 for logical drives and
158762306a36Sopenharmony_ci			 * requests for more than 40 logical drives
158862306a36Sopenharmony_ci			 */
158962306a36Sopenharmony_ci			if (SCP2LUN(scp)) {
159062306a36Sopenharmony_ci				scp->result = (DID_BAD_TARGET << 16);
159162306a36Sopenharmony_ci				return NULL;
159262306a36Sopenharmony_ci			}
159362306a36Sopenharmony_ci			if ((target % 0x80) >= MAX_LOGICAL_DRIVES_40LD) {
159462306a36Sopenharmony_ci				scp->result = (DID_BAD_TARGET << 16);
159562306a36Sopenharmony_ci				return NULL;
159662306a36Sopenharmony_ci			}
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci			/* Allocate a SCB and initialize passthru */
160062306a36Sopenharmony_ci			if (!(scb = megaraid_alloc_scb(adapter, scp))) {
160162306a36Sopenharmony_ci				scp->result = (DID_ERROR << 16);
160262306a36Sopenharmony_ci				*busy = 1;
160362306a36Sopenharmony_ci				return NULL;
160462306a36Sopenharmony_ci			}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci			ccb			= (mbox_ccb_t *)scb->ccb;
160762306a36Sopenharmony_ci			scb->dev_channel	= 0xFF;
160862306a36Sopenharmony_ci			scb->dev_target		= target;
160962306a36Sopenharmony_ci			pthru			= ccb->pthru;
161062306a36Sopenharmony_ci			mbox			= ccb->mbox;
161162306a36Sopenharmony_ci			mbox64			= ccb->mbox64;
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci			pthru->timeout		= 0;
161462306a36Sopenharmony_ci			pthru->ars		= 1;
161562306a36Sopenharmony_ci			pthru->reqsenselen	= 14;
161662306a36Sopenharmony_ci			pthru->islogical	= 1;
161762306a36Sopenharmony_ci			pthru->logdrv		= target;
161862306a36Sopenharmony_ci			pthru->cdblen		= scp->cmd_len;
161962306a36Sopenharmony_ci			memcpy(pthru->cdb, scp->cmnd, scp->cmd_len);
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci			mbox->cmd		= MBOXCMD_PASSTHRU64;
162262306a36Sopenharmony_ci			scb->dma_direction	= scp->sc_data_direction;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci			pthru->dataxferlen	= scsi_bufflen(scp);
162562306a36Sopenharmony_ci			pthru->dataxferaddr	= ccb->sgl_dma_h;
162662306a36Sopenharmony_ci			pthru->numsge		= megaraid_mbox_mksgl(adapter,
162762306a36Sopenharmony_ci							scb);
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci			mbox->xferaddr		= 0xFFFFFFFF;
163062306a36Sopenharmony_ci			mbox64->xferaddr_lo	= (uint32_t )ccb->pthru_dma_h;
163162306a36Sopenharmony_ci			mbox64->xferaddr_hi	= 0;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci			return scb;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci		case READ_6:
163662306a36Sopenharmony_ci		case WRITE_6:
163762306a36Sopenharmony_ci		case READ_10:
163862306a36Sopenharmony_ci		case WRITE_10:
163962306a36Sopenharmony_ci		case READ_12:
164062306a36Sopenharmony_ci		case WRITE_12:
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci			/*
164362306a36Sopenharmony_ci			 * Allocate a SCB and initialize mailbox
164462306a36Sopenharmony_ci			 */
164562306a36Sopenharmony_ci			if (!(scb = megaraid_alloc_scb(adapter, scp))) {
164662306a36Sopenharmony_ci				scp->result = (DID_ERROR << 16);
164762306a36Sopenharmony_ci				*busy = 1;
164862306a36Sopenharmony_ci				return NULL;
164962306a36Sopenharmony_ci			}
165062306a36Sopenharmony_ci			ccb			= (mbox_ccb_t *)scb->ccb;
165162306a36Sopenharmony_ci			scb->dev_channel	= 0xFF;
165262306a36Sopenharmony_ci			scb->dev_target		= target;
165362306a36Sopenharmony_ci			mbox			= ccb->mbox;
165462306a36Sopenharmony_ci			mbox64			= ccb->mbox64;
165562306a36Sopenharmony_ci			mbox->logdrv		= target;
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci			/*
165862306a36Sopenharmony_ci			 * A little HACK: 2nd bit is zero for all scsi read
165962306a36Sopenharmony_ci			 * commands and is set for all scsi write commands
166062306a36Sopenharmony_ci			 */
166162306a36Sopenharmony_ci			mbox->cmd = (scp->cmnd[0] & 0x02) ?  MBOXCMD_LWRITE64:
166262306a36Sopenharmony_ci					MBOXCMD_LREAD64 ;
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci			/*
166562306a36Sopenharmony_ci			 * 6-byte READ(0x08) or WRITE(0x0A) cdb
166662306a36Sopenharmony_ci			 */
166762306a36Sopenharmony_ci			if (scp->cmd_len == 6) {
166862306a36Sopenharmony_ci				mbox->numsectors = (uint32_t)scp->cmnd[4];
166962306a36Sopenharmony_ci				mbox->lba =
167062306a36Sopenharmony_ci					((uint32_t)scp->cmnd[1] << 16)	|
167162306a36Sopenharmony_ci					((uint32_t)scp->cmnd[2] << 8)	|
167262306a36Sopenharmony_ci					(uint32_t)scp->cmnd[3];
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci				mbox->lba &= 0x1FFFFF;
167562306a36Sopenharmony_ci			}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci			/*
167862306a36Sopenharmony_ci			 * 10-byte READ(0x28) or WRITE(0x2A) cdb
167962306a36Sopenharmony_ci			 */
168062306a36Sopenharmony_ci			else if (scp->cmd_len == 10) {
168162306a36Sopenharmony_ci				mbox->numsectors =
168262306a36Sopenharmony_ci					(uint32_t)scp->cmnd[8] |
168362306a36Sopenharmony_ci					((uint32_t)scp->cmnd[7] << 8);
168462306a36Sopenharmony_ci				mbox->lba =
168562306a36Sopenharmony_ci					((uint32_t)scp->cmnd[2] << 24) |
168662306a36Sopenharmony_ci					((uint32_t)scp->cmnd[3] << 16) |
168762306a36Sopenharmony_ci					((uint32_t)scp->cmnd[4] << 8) |
168862306a36Sopenharmony_ci					(uint32_t)scp->cmnd[5];
168962306a36Sopenharmony_ci			}
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci			/*
169262306a36Sopenharmony_ci			 * 12-byte READ(0xA8) or WRITE(0xAA) cdb
169362306a36Sopenharmony_ci			 */
169462306a36Sopenharmony_ci			else if (scp->cmd_len == 12) {
169562306a36Sopenharmony_ci				mbox->lba =
169662306a36Sopenharmony_ci					((uint32_t)scp->cmnd[2] << 24) |
169762306a36Sopenharmony_ci					((uint32_t)scp->cmnd[3] << 16) |
169862306a36Sopenharmony_ci					((uint32_t)scp->cmnd[4] << 8) |
169962306a36Sopenharmony_ci					(uint32_t)scp->cmnd[5];
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci				mbox->numsectors =
170262306a36Sopenharmony_ci					((uint32_t)scp->cmnd[6] << 24) |
170362306a36Sopenharmony_ci					((uint32_t)scp->cmnd[7] << 16) |
170462306a36Sopenharmony_ci					((uint32_t)scp->cmnd[8] << 8) |
170562306a36Sopenharmony_ci					(uint32_t)scp->cmnd[9];
170662306a36Sopenharmony_ci			}
170762306a36Sopenharmony_ci			else {
170862306a36Sopenharmony_ci				con_log(CL_ANN, (KERN_WARNING
170962306a36Sopenharmony_ci					"megaraid: unsupported CDB length\n"));
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci				megaraid_dealloc_scb(adapter, scb);
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci				scp->result = (DID_ERROR << 16);
171462306a36Sopenharmony_ci				return NULL;
171562306a36Sopenharmony_ci			}
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci			scb->dma_direction = scp->sc_data_direction;
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci			// Calculate Scatter-Gather info
172062306a36Sopenharmony_ci			mbox64->xferaddr_lo	= (uint32_t )ccb->sgl_dma_h;
172162306a36Sopenharmony_ci			mbox->numsge		= megaraid_mbox_mksgl(adapter,
172262306a36Sopenharmony_ci							scb);
172362306a36Sopenharmony_ci			mbox->xferaddr		= 0xFFFFFFFF;
172462306a36Sopenharmony_ci			mbox64->xferaddr_hi	= 0;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci			return scb;
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci		case RESERVE:
172962306a36Sopenharmony_ci		case RELEASE:
173062306a36Sopenharmony_ci			/*
173162306a36Sopenharmony_ci			 * Do we support clustering and is the support enabled
173262306a36Sopenharmony_ci			 */
173362306a36Sopenharmony_ci			if (!adapter->ha) {
173462306a36Sopenharmony_ci				scp->result = (DID_BAD_TARGET << 16);
173562306a36Sopenharmony_ci				return NULL;
173662306a36Sopenharmony_ci			}
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci			/*
173962306a36Sopenharmony_ci			 * Allocate a SCB and initialize mailbox
174062306a36Sopenharmony_ci			 */
174162306a36Sopenharmony_ci			if (!(scb = megaraid_alloc_scb(adapter, scp))) {
174262306a36Sopenharmony_ci				scp->result = (DID_ERROR << 16);
174362306a36Sopenharmony_ci				*busy = 1;
174462306a36Sopenharmony_ci				return NULL;
174562306a36Sopenharmony_ci			}
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci			ccb			= (mbox_ccb_t *)scb->ccb;
174862306a36Sopenharmony_ci			scb->dev_channel	= 0xFF;
174962306a36Sopenharmony_ci			scb->dev_target		= target;
175062306a36Sopenharmony_ci			ccb->raw_mbox[0]	= CLUSTER_CMD;
175162306a36Sopenharmony_ci			ccb->raw_mbox[2]	=  (scp->cmnd[0] == RESERVE) ?
175262306a36Sopenharmony_ci						RESERVE_LD : RELEASE_LD;
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci			ccb->raw_mbox[3]	= target;
175562306a36Sopenharmony_ci			scb->dma_direction	= scp->sc_data_direction;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci			return scb;
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci		default:
176062306a36Sopenharmony_ci			scp->result = (DID_BAD_TARGET << 16);
176162306a36Sopenharmony_ci			return NULL;
176262306a36Sopenharmony_ci		}
176362306a36Sopenharmony_ci	}
176462306a36Sopenharmony_ci	else { // Passthru device commands
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci		// Do not allow access to target id > 15 or LUN > 7
176762306a36Sopenharmony_ci		if (target > 15 || SCP2LUN(scp) > 7) {
176862306a36Sopenharmony_ci			scp->result = (DID_BAD_TARGET << 16);
176962306a36Sopenharmony_ci			return NULL;
177062306a36Sopenharmony_ci		}
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci		// if fast load option was set and scan for last device is
177362306a36Sopenharmony_ci		// over, reset the fast_load flag so that during a possible
177462306a36Sopenharmony_ci		// next scan, devices can be made available
177562306a36Sopenharmony_ci		if (rdev->fast_load && (target == 15) &&
177662306a36Sopenharmony_ci			(SCP2CHANNEL(scp) == adapter->max_channel -1)) {
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_INFO
177962306a36Sopenharmony_ci			"megaraid[%d]: physical device scan re-enabled\n",
178062306a36Sopenharmony_ci				adapter->host->host_no));
178162306a36Sopenharmony_ci			rdev->fast_load = 0;
178262306a36Sopenharmony_ci		}
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci		/*
178562306a36Sopenharmony_ci		 * Display the channel scan for physical devices
178662306a36Sopenharmony_ci		 */
178762306a36Sopenharmony_ci		if (!(rdev->last_disp & (1L << SCP2CHANNEL(scp)))) {
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci			ss = rdev->fast_load ? skip : scan;
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_INFO
179262306a36Sopenharmony_ci				"scsi[%d]: %s scsi channel %d [Phy %d]",
179362306a36Sopenharmony_ci				adapter->host->host_no, ss, SCP2CHANNEL(scp),
179462306a36Sopenharmony_ci				channel));
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci			con_log(CL_ANN, (
179762306a36Sopenharmony_ci				" for non-raid devices\n"));
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci			rdev->last_disp |= (1L << SCP2CHANNEL(scp));
180062306a36Sopenharmony_ci		}
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci		// disable channel sweep if fast load option given
180362306a36Sopenharmony_ci		if (rdev->fast_load) {
180462306a36Sopenharmony_ci			scp->result = (DID_BAD_TARGET << 16);
180562306a36Sopenharmony_ci			return NULL;
180662306a36Sopenharmony_ci		}
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci		// Allocate a SCB and initialize passthru
180962306a36Sopenharmony_ci		if (!(scb = megaraid_alloc_scb(adapter, scp))) {
181062306a36Sopenharmony_ci			scp->result = (DID_ERROR << 16);
181162306a36Sopenharmony_ci			*busy = 1;
181262306a36Sopenharmony_ci			return NULL;
181362306a36Sopenharmony_ci		}
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci		ccb			= (mbox_ccb_t *)scb->ccb;
181662306a36Sopenharmony_ci		scb->dev_channel	= channel;
181762306a36Sopenharmony_ci		scb->dev_target		= target;
181862306a36Sopenharmony_ci		scb->dma_direction	= scp->sc_data_direction;
181962306a36Sopenharmony_ci		mbox			= ccb->mbox;
182062306a36Sopenharmony_ci		mbox64			= ccb->mbox64;
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci		// Does this firmware support extended CDBs
182362306a36Sopenharmony_ci		if (adapter->max_cdb_sz == 16) {
182462306a36Sopenharmony_ci			mbox->cmd		= MBOXCMD_EXTPTHRU;
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci			megaraid_mbox_prepare_epthru(adapter, scb, scp);
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci			mbox64->xferaddr_lo	= (uint32_t)ccb->epthru_dma_h;
182962306a36Sopenharmony_ci			mbox64->xferaddr_hi	= 0;
183062306a36Sopenharmony_ci			mbox->xferaddr		= 0xFFFFFFFF;
183162306a36Sopenharmony_ci		}
183262306a36Sopenharmony_ci		else {
183362306a36Sopenharmony_ci			mbox->cmd = MBOXCMD_PASSTHRU64;
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci			megaraid_mbox_prepare_pthru(adapter, scb, scp);
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci			mbox64->xferaddr_lo	= (uint32_t)ccb->pthru_dma_h;
183862306a36Sopenharmony_ci			mbox64->xferaddr_hi	= 0;
183962306a36Sopenharmony_ci			mbox->xferaddr		= 0xFFFFFFFF;
184062306a36Sopenharmony_ci		}
184162306a36Sopenharmony_ci		return scb;
184262306a36Sopenharmony_ci	}
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	// NOT REACHED
184562306a36Sopenharmony_ci}
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci/**
184962306a36Sopenharmony_ci * megaraid_mbox_runpendq - execute commands queued in the pending queue
185062306a36Sopenharmony_ci * @adapter	: controller's soft state
185162306a36Sopenharmony_ci * @scb_q	: SCB to be queued in the pending list
185262306a36Sopenharmony_ci *
185362306a36Sopenharmony_ci * Scan the pending list for commands which are not yet issued and try to
185462306a36Sopenharmony_ci * post to the controller. The SCB can be a null pointer, which would indicate
185562306a36Sopenharmony_ci * no SCB to be queue, just try to execute the ones in the pending list.
185662306a36Sopenharmony_ci *
185762306a36Sopenharmony_ci * NOTE: We do not actually traverse the pending list. The SCBs are plucked
185862306a36Sopenharmony_ci * out from the head of the pending list. If it is successfully issued, the
185962306a36Sopenharmony_ci * next SCB is at the head now.
186062306a36Sopenharmony_ci */
186162306a36Sopenharmony_cistatic void
186262306a36Sopenharmony_cimegaraid_mbox_runpendq(adapter_t *adapter, scb_t *scb_q)
186362306a36Sopenharmony_ci{
186462306a36Sopenharmony_ci	scb_t			*scb;
186562306a36Sopenharmony_ci	unsigned long		flags;
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags);
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	if (scb_q) {
187062306a36Sopenharmony_ci		scb_q->state = SCB_PENDQ;
187162306a36Sopenharmony_ci		list_add_tail(&scb_q->list, &adapter->pend_list);
187262306a36Sopenharmony_ci	}
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci	// if the adapter in not in quiescent mode, post the commands to FW
187562306a36Sopenharmony_ci	if (adapter->quiescent) {
187662306a36Sopenharmony_ci		spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags);
187762306a36Sopenharmony_ci		return;
187862306a36Sopenharmony_ci	}
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	while (!list_empty(&adapter->pend_list)) {
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci		assert_spin_locked(PENDING_LIST_LOCK(adapter));
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci		scb = list_entry(adapter->pend_list.next, scb_t, list);
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci		// remove the scb from the pending list and try to
188762306a36Sopenharmony_ci		// issue. If we are unable to issue it, put back in
188862306a36Sopenharmony_ci		// the pending list and return
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci		list_del_init(&scb->list);
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci		spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags);
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci		// if mailbox was busy, return SCB back to pending
189562306a36Sopenharmony_ci		// list. Make sure to add at the head, since that's
189662306a36Sopenharmony_ci		// where it would have been removed from
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci		scb->state = SCB_ISSUED;
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci		if (mbox_post_cmd(adapter, scb) != 0) {
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci			spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags);
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci			scb->state = SCB_PENDQ;
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci			list_add(&scb->list, &adapter->pend_list);
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci			spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter),
190962306a36Sopenharmony_ci				flags);
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci			return;
191262306a36Sopenharmony_ci		}
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci		spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags);
191562306a36Sopenharmony_ci	}
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci	spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags);
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	return;
192162306a36Sopenharmony_ci}
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci/**
192562306a36Sopenharmony_ci * megaraid_mbox_prepare_pthru - prepare a command for physical devices
192662306a36Sopenharmony_ci * @adapter	: pointer to controller's soft state
192762306a36Sopenharmony_ci * @scb		: scsi control block
192862306a36Sopenharmony_ci * @scp		: scsi command from the mid-layer
192962306a36Sopenharmony_ci *
193062306a36Sopenharmony_ci * Prepare a command for the scsi physical devices.
193162306a36Sopenharmony_ci */
193262306a36Sopenharmony_cistatic void
193362306a36Sopenharmony_cimegaraid_mbox_prepare_pthru(adapter_t *adapter, scb_t *scb,
193462306a36Sopenharmony_ci		struct scsi_cmnd *scp)
193562306a36Sopenharmony_ci{
193662306a36Sopenharmony_ci	mbox_ccb_t		*ccb;
193762306a36Sopenharmony_ci	mraid_passthru_t	*pthru;
193862306a36Sopenharmony_ci	uint8_t			channel;
193962306a36Sopenharmony_ci	uint8_t			target;
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	ccb	= (mbox_ccb_t *)scb->ccb;
194262306a36Sopenharmony_ci	pthru	= ccb->pthru;
194362306a36Sopenharmony_ci	channel	= scb->dev_channel;
194462306a36Sopenharmony_ci	target	= scb->dev_target;
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	// 0=6sec, 1=60sec, 2=10min, 3=3hrs, 4=NO timeout
194762306a36Sopenharmony_ci	pthru->timeout		= 4;
194862306a36Sopenharmony_ci	pthru->ars		= 1;
194962306a36Sopenharmony_ci	pthru->islogical	= 0;
195062306a36Sopenharmony_ci	pthru->channel		= 0;
195162306a36Sopenharmony_ci	pthru->target		= (channel << 4) | target;
195262306a36Sopenharmony_ci	pthru->logdrv		= SCP2LUN(scp);
195362306a36Sopenharmony_ci	pthru->reqsenselen	= 14;
195462306a36Sopenharmony_ci	pthru->cdblen		= scp->cmd_len;
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci	memcpy(pthru->cdb, scp->cmnd, scp->cmd_len);
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	if (scsi_bufflen(scp)) {
195962306a36Sopenharmony_ci		pthru->dataxferlen	= scsi_bufflen(scp);
196062306a36Sopenharmony_ci		pthru->dataxferaddr	= ccb->sgl_dma_h;
196162306a36Sopenharmony_ci		pthru->numsge		= megaraid_mbox_mksgl(adapter, scb);
196262306a36Sopenharmony_ci	}
196362306a36Sopenharmony_ci	else {
196462306a36Sopenharmony_ci		pthru->dataxferaddr	= 0;
196562306a36Sopenharmony_ci		pthru->dataxferlen	= 0;
196662306a36Sopenharmony_ci		pthru->numsge		= 0;
196762306a36Sopenharmony_ci	}
196862306a36Sopenharmony_ci	return;
196962306a36Sopenharmony_ci}
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci/**
197362306a36Sopenharmony_ci * megaraid_mbox_prepare_epthru - prepare a command for physical devices
197462306a36Sopenharmony_ci * @adapter	: pointer to controller's soft state
197562306a36Sopenharmony_ci * @scb		: scsi control block
197662306a36Sopenharmony_ci * @scp		: scsi command from the mid-layer
197762306a36Sopenharmony_ci *
197862306a36Sopenharmony_ci * Prepare a command for the scsi physical devices. This routine prepares
197962306a36Sopenharmony_ci * commands for devices which can take extended CDBs (>10 bytes).
198062306a36Sopenharmony_ci */
198162306a36Sopenharmony_cistatic void
198262306a36Sopenharmony_cimegaraid_mbox_prepare_epthru(adapter_t *adapter, scb_t *scb,
198362306a36Sopenharmony_ci		struct scsi_cmnd *scp)
198462306a36Sopenharmony_ci{
198562306a36Sopenharmony_ci	mbox_ccb_t		*ccb;
198662306a36Sopenharmony_ci	mraid_epassthru_t	*epthru;
198762306a36Sopenharmony_ci	uint8_t			channel;
198862306a36Sopenharmony_ci	uint8_t			target;
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	ccb	= (mbox_ccb_t *)scb->ccb;
199162306a36Sopenharmony_ci	epthru	= ccb->epthru;
199262306a36Sopenharmony_ci	channel	= scb->dev_channel;
199362306a36Sopenharmony_ci	target	= scb->dev_target;
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci	// 0=6sec, 1=60sec, 2=10min, 3=3hrs, 4=NO timeout
199662306a36Sopenharmony_ci	epthru->timeout		= 4;
199762306a36Sopenharmony_ci	epthru->ars		= 1;
199862306a36Sopenharmony_ci	epthru->islogical	= 0;
199962306a36Sopenharmony_ci	epthru->channel		= 0;
200062306a36Sopenharmony_ci	epthru->target		= (channel << 4) | target;
200162306a36Sopenharmony_ci	epthru->logdrv		= SCP2LUN(scp);
200262306a36Sopenharmony_ci	epthru->reqsenselen	= 14;
200362306a36Sopenharmony_ci	epthru->cdblen		= scp->cmd_len;
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	memcpy(epthru->cdb, scp->cmnd, scp->cmd_len);
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci	if (scsi_bufflen(scp)) {
200862306a36Sopenharmony_ci		epthru->dataxferlen	= scsi_bufflen(scp);
200962306a36Sopenharmony_ci		epthru->dataxferaddr	= ccb->sgl_dma_h;
201062306a36Sopenharmony_ci		epthru->numsge		= megaraid_mbox_mksgl(adapter, scb);
201162306a36Sopenharmony_ci	}
201262306a36Sopenharmony_ci	else {
201362306a36Sopenharmony_ci		epthru->dataxferaddr	= 0;
201462306a36Sopenharmony_ci		epthru->dataxferlen	= 0;
201562306a36Sopenharmony_ci		epthru->numsge		= 0;
201662306a36Sopenharmony_ci	}
201762306a36Sopenharmony_ci	return;
201862306a36Sopenharmony_ci}
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci/**
202262306a36Sopenharmony_ci * megaraid_ack_sequence - interrupt ack sequence for memory mapped HBAs
202362306a36Sopenharmony_ci * @adapter	: controller's soft state
202462306a36Sopenharmony_ci *
202562306a36Sopenharmony_ci * Interrupt acknowledgement sequence for memory mapped HBAs. Find out the
202662306a36Sopenharmony_ci * completed command and put them on the completed list for later processing.
202762306a36Sopenharmony_ci *
202862306a36Sopenharmony_ci * Returns:	1 if the interrupt is valid, 0 otherwise
202962306a36Sopenharmony_ci */
203062306a36Sopenharmony_cistatic int
203162306a36Sopenharmony_cimegaraid_ack_sequence(adapter_t *adapter)
203262306a36Sopenharmony_ci{
203362306a36Sopenharmony_ci	mraid_device_t		*raid_dev = ADAP2RAIDDEV(adapter);
203462306a36Sopenharmony_ci	mbox_t			*mbox;
203562306a36Sopenharmony_ci	scb_t			*scb;
203662306a36Sopenharmony_ci	uint8_t			nstatus;
203762306a36Sopenharmony_ci	uint8_t			completed[MBOX_MAX_FIRMWARE_STATUS];
203862306a36Sopenharmony_ci	struct list_head	clist;
203962306a36Sopenharmony_ci	int			handled;
204062306a36Sopenharmony_ci	uint32_t		dword;
204162306a36Sopenharmony_ci	unsigned long		flags;
204262306a36Sopenharmony_ci	int			i, j;
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci	mbox	= raid_dev->mbox;
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci	// move the SCBs from the firmware completed array to our local list
204862306a36Sopenharmony_ci	INIT_LIST_HEAD(&clist);
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	// loop till F/W has more commands for us to complete
205162306a36Sopenharmony_ci	handled = 0;
205262306a36Sopenharmony_ci	spin_lock_irqsave(MAILBOX_LOCK(raid_dev), flags);
205362306a36Sopenharmony_ci	do {
205462306a36Sopenharmony_ci		/*
205562306a36Sopenharmony_ci		 * Check if a valid interrupt is pending. If found, force the
205662306a36Sopenharmony_ci		 * interrupt line low.
205762306a36Sopenharmony_ci		 */
205862306a36Sopenharmony_ci		dword = RDOUTDOOR(raid_dev);
205962306a36Sopenharmony_ci		if (dword != 0x10001234) break;
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci		handled = 1;
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci		WROUTDOOR(raid_dev, 0x10001234);
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci		nstatus = 0;
206662306a36Sopenharmony_ci		// wait for valid numstatus to post
206762306a36Sopenharmony_ci		for (i = 0; i < 0xFFFFF; i++) {
206862306a36Sopenharmony_ci			if (mbox->numstatus != 0xFF) {
206962306a36Sopenharmony_ci				nstatus = mbox->numstatus;
207062306a36Sopenharmony_ci				break;
207162306a36Sopenharmony_ci			}
207262306a36Sopenharmony_ci			rmb();
207362306a36Sopenharmony_ci		}
207462306a36Sopenharmony_ci		mbox->numstatus = 0xFF;
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci		adapter->outstanding_cmds -= nstatus;
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci		for (i = 0; i < nstatus; i++) {
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci			// wait for valid command index to post
208162306a36Sopenharmony_ci			for (j = 0; j < 0xFFFFF; j++) {
208262306a36Sopenharmony_ci				if (mbox->completed[i] != 0xFF) break;
208362306a36Sopenharmony_ci				rmb();
208462306a36Sopenharmony_ci			}
208562306a36Sopenharmony_ci			completed[i]		= mbox->completed[i];
208662306a36Sopenharmony_ci			mbox->completed[i]	= 0xFF;
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci			if (completed[i] == 0xFF) {
208962306a36Sopenharmony_ci				con_log(CL_ANN, (KERN_CRIT
209062306a36Sopenharmony_ci				"megaraid: command posting timed out\n"));
209162306a36Sopenharmony_ci
209262306a36Sopenharmony_ci				BUG();
209362306a36Sopenharmony_ci				continue;
209462306a36Sopenharmony_ci			}
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci			// Get SCB associated with this command id
209762306a36Sopenharmony_ci			if (completed[i] >= MBOX_MAX_SCSI_CMDS) {
209862306a36Sopenharmony_ci				// a cmm command
209962306a36Sopenharmony_ci				scb = adapter->uscb_list + (completed[i] -
210062306a36Sopenharmony_ci						MBOX_MAX_SCSI_CMDS);
210162306a36Sopenharmony_ci			}
210262306a36Sopenharmony_ci			else {
210362306a36Sopenharmony_ci				// an os command
210462306a36Sopenharmony_ci				scb = adapter->kscb_list + completed[i];
210562306a36Sopenharmony_ci			}
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci			scb->status = mbox->status;
210862306a36Sopenharmony_ci			list_add_tail(&scb->list, &clist);
210962306a36Sopenharmony_ci		}
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci		// Acknowledge interrupt
211262306a36Sopenharmony_ci		WRINDOOR(raid_dev, 0x02);
211362306a36Sopenharmony_ci
211462306a36Sopenharmony_ci	} while(1);
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	spin_unlock_irqrestore(MAILBOX_LOCK(raid_dev), flags);
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci	// put the completed commands in the completed list. DPC would
212062306a36Sopenharmony_ci	// complete these commands later
212162306a36Sopenharmony_ci	spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags);
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci	list_splice(&clist, &adapter->completed_list);
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags);
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci	// schedule the DPC if there is some work for it
212962306a36Sopenharmony_ci	if (handled)
213062306a36Sopenharmony_ci		tasklet_schedule(&adapter->dpc_h);
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	return handled;
213362306a36Sopenharmony_ci}
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci/**
213762306a36Sopenharmony_ci * megaraid_isr - isr for memory based mailbox based controllers
213862306a36Sopenharmony_ci * @irq		: irq
213962306a36Sopenharmony_ci * @devp	: pointer to our soft state
214062306a36Sopenharmony_ci *
214162306a36Sopenharmony_ci * Interrupt service routine for memory-mapped mailbox controllers.
214262306a36Sopenharmony_ci */
214362306a36Sopenharmony_cistatic irqreturn_t
214462306a36Sopenharmony_cimegaraid_isr(int irq, void *devp)
214562306a36Sopenharmony_ci{
214662306a36Sopenharmony_ci	adapter_t	*adapter = devp;
214762306a36Sopenharmony_ci	int		handled;
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	handled = megaraid_ack_sequence(adapter);
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	/* Loop through any pending requests */
215262306a36Sopenharmony_ci	if (!adapter->quiescent) {
215362306a36Sopenharmony_ci		megaraid_mbox_runpendq(adapter, NULL);
215462306a36Sopenharmony_ci	}
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	return IRQ_RETVAL(handled);
215762306a36Sopenharmony_ci}
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci/**
216162306a36Sopenharmony_ci * megaraid_mbox_dpc - the tasklet to complete the commands from completed list
216262306a36Sopenharmony_ci * @devp	: pointer to HBA soft state
216362306a36Sopenharmony_ci *
216462306a36Sopenharmony_ci * Pick up the commands from the completed list and send back to the owners.
216562306a36Sopenharmony_ci * This is a reentrant function and does not assume any locks are held while
216662306a36Sopenharmony_ci * it is being called.
216762306a36Sopenharmony_ci */
216862306a36Sopenharmony_cistatic void
216962306a36Sopenharmony_cimegaraid_mbox_dpc(unsigned long devp)
217062306a36Sopenharmony_ci{
217162306a36Sopenharmony_ci	adapter_t		*adapter = (adapter_t *)devp;
217262306a36Sopenharmony_ci	mraid_device_t		*raid_dev;
217362306a36Sopenharmony_ci	struct list_head	clist;
217462306a36Sopenharmony_ci	struct scatterlist	*sgl;
217562306a36Sopenharmony_ci	scb_t			*scb;
217662306a36Sopenharmony_ci	scb_t			*tmp;
217762306a36Sopenharmony_ci	struct scsi_cmnd	*scp;
217862306a36Sopenharmony_ci	mraid_passthru_t	*pthru;
217962306a36Sopenharmony_ci	mraid_epassthru_t	*epthru;
218062306a36Sopenharmony_ci	mbox_ccb_t		*ccb;
218162306a36Sopenharmony_ci	int			islogical;
218262306a36Sopenharmony_ci	int			pdev_index;
218362306a36Sopenharmony_ci	int			pdev_state;
218462306a36Sopenharmony_ci	mbox_t			*mbox;
218562306a36Sopenharmony_ci	unsigned long		flags;
218662306a36Sopenharmony_ci	uint8_t			c;
218762306a36Sopenharmony_ci	int			status;
218862306a36Sopenharmony_ci	uioc_t			*kioc;
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci	if (!adapter) return;
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	raid_dev = ADAP2RAIDDEV(adapter);
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	// move the SCBs from the completed list to our local list
219662306a36Sopenharmony_ci	INIT_LIST_HEAD(&clist);
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags);
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	list_splice_init(&adapter->completed_list, &clist);
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci	spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags);
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	list_for_each_entry_safe(scb, tmp, &clist, list) {
220662306a36Sopenharmony_ci
220762306a36Sopenharmony_ci		status		= scb->status;
220862306a36Sopenharmony_ci		scp		= scb->scp;
220962306a36Sopenharmony_ci		ccb		= (mbox_ccb_t *)scb->ccb;
221062306a36Sopenharmony_ci		pthru		= ccb->pthru;
221162306a36Sopenharmony_ci		epthru		= ccb->epthru;
221262306a36Sopenharmony_ci		mbox		= ccb->mbox;
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci		// Make sure f/w has completed a valid command
221562306a36Sopenharmony_ci		if (scb->state != SCB_ISSUED) {
221662306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_CRIT
221762306a36Sopenharmony_ci			"megaraid critical err: invalid command %d:%d:%p\n",
221862306a36Sopenharmony_ci				scb->sno, scb->state, scp));
221962306a36Sopenharmony_ci			BUG();
222062306a36Sopenharmony_ci			continue;	// Must never happen!
222162306a36Sopenharmony_ci		}
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci		// check for the management command and complete it right away
222462306a36Sopenharmony_ci		if (scb->sno >= MBOX_MAX_SCSI_CMDS) {
222562306a36Sopenharmony_ci			scb->state	= SCB_FREE;
222662306a36Sopenharmony_ci			scb->status	= status;
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci			// remove from local clist
222962306a36Sopenharmony_ci			list_del_init(&scb->list);
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ci			kioc			= (uioc_t *)scb->gp;
223262306a36Sopenharmony_ci			kioc->status		= 0;
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_ci			megaraid_mbox_mm_done(adapter, scb);
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci			continue;
223762306a36Sopenharmony_ci		}
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_ci		// Was an abort issued for this command earlier
224062306a36Sopenharmony_ci		if (scb->state & SCB_ABORT) {
224162306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_NOTICE
224262306a36Sopenharmony_ci			"megaraid: aborted cmd [%x] completed\n",
224362306a36Sopenharmony_ci				scb->sno));
224462306a36Sopenharmony_ci		}
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci		/*
224762306a36Sopenharmony_ci		 * If the inquiry came of a disk drive which is not part of
224862306a36Sopenharmony_ci		 * any RAID array, expose it to the kernel. For this to be
224962306a36Sopenharmony_ci		 * enabled, user must set the "megaraid_expose_unconf_disks"
225062306a36Sopenharmony_ci		 * flag to 1 by specifying it on module parameter list.
225162306a36Sopenharmony_ci		 * This would enable data migration off drives from other
225262306a36Sopenharmony_ci		 * configurations.
225362306a36Sopenharmony_ci		 */
225462306a36Sopenharmony_ci		islogical = MRAID_IS_LOGICAL(adapter, scp);
225562306a36Sopenharmony_ci		if (scp->cmnd[0] == INQUIRY && status == 0 && islogical == 0
225662306a36Sopenharmony_ci				&& IS_RAID_CH(raid_dev, scb->dev_channel)) {
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_ci			sgl = scsi_sglist(scp);
225962306a36Sopenharmony_ci			if (sg_page(sgl)) {
226062306a36Sopenharmony_ci				c = *(unsigned char *) sg_virt(&sgl[0]);
226162306a36Sopenharmony_ci			} else {
226262306a36Sopenharmony_ci				con_log(CL_ANN, (KERN_WARNING
226362306a36Sopenharmony_ci						 "megaraid mailbox: invalid sg:%d\n",
226462306a36Sopenharmony_ci						 __LINE__));
226562306a36Sopenharmony_ci				c = 0;
226662306a36Sopenharmony_ci			}
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci			if ((c & 0x1F ) == TYPE_DISK) {
226962306a36Sopenharmony_ci				pdev_index = (scb->dev_channel * 16) +
227062306a36Sopenharmony_ci					scb->dev_target;
227162306a36Sopenharmony_ci				pdev_state =
227262306a36Sopenharmony_ci					raid_dev->pdrv_state[pdev_index] & 0x0F;
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci				if (pdev_state == PDRV_ONLINE		||
227562306a36Sopenharmony_ci					pdev_state == PDRV_FAILED	||
227662306a36Sopenharmony_ci					pdev_state == PDRV_RBLD		||
227762306a36Sopenharmony_ci					pdev_state == PDRV_HOTSPARE	||
227862306a36Sopenharmony_ci					megaraid_expose_unconf_disks == 0) {
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci					status = 0xF0;
228162306a36Sopenharmony_ci				}
228262306a36Sopenharmony_ci			}
228362306a36Sopenharmony_ci		}
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci		// Convert MegaRAID status to Linux error code
228662306a36Sopenharmony_ci		switch (status) {
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci		case 0x00:
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci			scp->result = (DID_OK << 16);
229162306a36Sopenharmony_ci			break;
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci		case 0x02:
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_ci			/* set sense_buffer and result fields */
229662306a36Sopenharmony_ci			if (mbox->cmd == MBOXCMD_PASSTHRU ||
229762306a36Sopenharmony_ci				mbox->cmd == MBOXCMD_PASSTHRU64) {
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci				memcpy(scp->sense_buffer, pthru->reqsensearea,
230062306a36Sopenharmony_ci						14);
230162306a36Sopenharmony_ci
230262306a36Sopenharmony_ci				scp->result = SAM_STAT_CHECK_CONDITION;
230362306a36Sopenharmony_ci			}
230462306a36Sopenharmony_ci			else {
230562306a36Sopenharmony_ci				if (mbox->cmd == MBOXCMD_EXTPTHRU) {
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci					memcpy(scp->sense_buffer,
230862306a36Sopenharmony_ci						epthru->reqsensearea, 14);
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ci					scp->result = SAM_STAT_CHECK_CONDITION;
231162306a36Sopenharmony_ci				} else
231262306a36Sopenharmony_ci					scsi_build_sense(scp, 0,
231362306a36Sopenharmony_ci							 ABORTED_COMMAND, 0, 0);
231462306a36Sopenharmony_ci			}
231562306a36Sopenharmony_ci			break;
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci		case 0x08:
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci			scp->result = DID_BUS_BUSY << 16 | status;
232062306a36Sopenharmony_ci			break;
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_ci		default:
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci			/*
232562306a36Sopenharmony_ci			 * If TEST_UNIT_READY fails, we know RESERVATION_STATUS
232662306a36Sopenharmony_ci			 * failed
232762306a36Sopenharmony_ci			 */
232862306a36Sopenharmony_ci			if (scp->cmnd[0] == TEST_UNIT_READY) {
232962306a36Sopenharmony_ci				scp->result = DID_ERROR << 16 |
233062306a36Sopenharmony_ci					SAM_STAT_RESERVATION_CONFLICT;
233162306a36Sopenharmony_ci			}
233262306a36Sopenharmony_ci			else
233362306a36Sopenharmony_ci			/*
233462306a36Sopenharmony_ci			 * Error code returned is 1 if Reserve or Release
233562306a36Sopenharmony_ci			 * failed or the input parameter is invalid
233662306a36Sopenharmony_ci			 */
233762306a36Sopenharmony_ci			if (status == 1 && (scp->cmnd[0] == RESERVE ||
233862306a36Sopenharmony_ci					 scp->cmnd[0] == RELEASE)) {
233962306a36Sopenharmony_ci
234062306a36Sopenharmony_ci				scp->result = DID_ERROR << 16 |
234162306a36Sopenharmony_ci					SAM_STAT_RESERVATION_CONFLICT;
234262306a36Sopenharmony_ci			}
234362306a36Sopenharmony_ci			else {
234462306a36Sopenharmony_ci				scp->result = DID_BAD_TARGET << 16 | status;
234562306a36Sopenharmony_ci			}
234662306a36Sopenharmony_ci		}
234762306a36Sopenharmony_ci
234862306a36Sopenharmony_ci		// print a debug message for all failed commands
234962306a36Sopenharmony_ci		if (status) {
235062306a36Sopenharmony_ci			megaraid_mbox_display_scb(adapter, scb);
235162306a36Sopenharmony_ci		}
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci		scsi_dma_unmap(scp);
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_ci		// remove from local clist
235662306a36Sopenharmony_ci		list_del_init(&scb->list);
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ci		// put back in free list
235962306a36Sopenharmony_ci		megaraid_dealloc_scb(adapter, scb);
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci		// send the scsi packet back to kernel
236262306a36Sopenharmony_ci		scsi_done(scp);
236362306a36Sopenharmony_ci	}
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	return;
236662306a36Sopenharmony_ci}
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci/**
237062306a36Sopenharmony_ci * megaraid_abort_handler - abort the scsi command
237162306a36Sopenharmony_ci * @scp		: command to be aborted
237262306a36Sopenharmony_ci *
237362306a36Sopenharmony_ci * Abort a previous SCSI request. Only commands on the pending list can be
237462306a36Sopenharmony_ci * aborted. All the commands issued to the F/W must complete.
237562306a36Sopenharmony_ci **/
237662306a36Sopenharmony_cistatic int
237762306a36Sopenharmony_cimegaraid_abort_handler(struct scsi_cmnd *scp)
237862306a36Sopenharmony_ci{
237962306a36Sopenharmony_ci	adapter_t		*adapter;
238062306a36Sopenharmony_ci	mraid_device_t		*raid_dev;
238162306a36Sopenharmony_ci	scb_t			*scb;
238262306a36Sopenharmony_ci	scb_t			*tmp;
238362306a36Sopenharmony_ci	int			found;
238462306a36Sopenharmony_ci	unsigned long		flags;
238562306a36Sopenharmony_ci	int			i;
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	adapter		= SCP2ADAPTER(scp);
238962306a36Sopenharmony_ci	raid_dev	= ADAP2RAIDDEV(adapter);
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci	con_log(CL_ANN, (KERN_WARNING
239262306a36Sopenharmony_ci		"megaraid: aborting cmd=%x <c=%d t=%d l=%d>\n",
239362306a36Sopenharmony_ci		scp->cmnd[0], SCP2CHANNEL(scp),
239462306a36Sopenharmony_ci		SCP2TARGET(scp), SCP2LUN(scp)));
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_ci	// If FW has stopped responding, simply return failure
239762306a36Sopenharmony_ci	if (raid_dev->hw_error) {
239862306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_NOTICE
239962306a36Sopenharmony_ci			"megaraid: hw error, not aborting\n"));
240062306a36Sopenharmony_ci		return FAILED;
240162306a36Sopenharmony_ci	}
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci	// There might a race here, where the command was completed by the
240462306a36Sopenharmony_ci	// firmware and now it is on the completed list. Before we could
240562306a36Sopenharmony_ci	// complete the command to the kernel in dpc, the abort came.
240662306a36Sopenharmony_ci	// Find out if this is the case to avoid the race.
240762306a36Sopenharmony_ci	scb = NULL;
240862306a36Sopenharmony_ci	spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags);
240962306a36Sopenharmony_ci	list_for_each_entry_safe(scb, tmp, &adapter->completed_list, list) {
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci		if (scb->scp == scp) {	// Found command
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci			list_del_init(&scb->list);	// from completed list
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_WARNING
241662306a36Sopenharmony_ci			"megaraid: %d[%d:%d], abort from completed list\n",
241762306a36Sopenharmony_ci				scb->sno, scb->dev_channel, scb->dev_target));
241862306a36Sopenharmony_ci
241962306a36Sopenharmony_ci			scp->result = (DID_ABORT << 16);
242062306a36Sopenharmony_ci			scsi_done(scp);
242162306a36Sopenharmony_ci
242262306a36Sopenharmony_ci			megaraid_dealloc_scb(adapter, scb);
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_ci			spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter),
242562306a36Sopenharmony_ci				flags);
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci			return SUCCESS;
242862306a36Sopenharmony_ci		}
242962306a36Sopenharmony_ci	}
243062306a36Sopenharmony_ci	spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags);
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci
243362306a36Sopenharmony_ci	// Find out if this command is still on the pending list. If it is and
243462306a36Sopenharmony_ci	// was never issued, abort and return success. If the command is owned
243562306a36Sopenharmony_ci	// by the firmware, we must wait for it to complete by the FW.
243662306a36Sopenharmony_ci	spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags);
243762306a36Sopenharmony_ci	list_for_each_entry_safe(scb, tmp, &adapter->pend_list, list) {
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci		if (scb->scp == scp) {	// Found command
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci			list_del_init(&scb->list);	// from pending list
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci			ASSERT(!(scb->state & SCB_ISSUED));
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_WARNING
244662306a36Sopenharmony_ci				"megaraid abort: [%d:%d], driver owner\n",
244762306a36Sopenharmony_ci				scb->dev_channel, scb->dev_target));
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci			scp->result = (DID_ABORT << 16);
245062306a36Sopenharmony_ci			scsi_done(scp);
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci			megaraid_dealloc_scb(adapter, scb);
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci			spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter),
245562306a36Sopenharmony_ci				flags);
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_ci			return SUCCESS;
245862306a36Sopenharmony_ci		}
245962306a36Sopenharmony_ci	}
246062306a36Sopenharmony_ci	spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags);
246162306a36Sopenharmony_ci
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci	// Check do we even own this command, in which case this would be
246462306a36Sopenharmony_ci	// owned by the firmware. The only way to locate the FW scb is to
246562306a36Sopenharmony_ci	// traverse through the list of all SCB, since driver does not
246662306a36Sopenharmony_ci	// maintain these SCBs on any list
246762306a36Sopenharmony_ci	found = 0;
246862306a36Sopenharmony_ci	spin_lock_irq(&adapter->lock);
246962306a36Sopenharmony_ci	for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
247062306a36Sopenharmony_ci		scb = adapter->kscb_list + i;
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_ci		if (scb->scp == scp) {
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci			found = 1;
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci			if (!(scb->state & SCB_ISSUED)) {
247762306a36Sopenharmony_ci				con_log(CL_ANN, (KERN_WARNING
247862306a36Sopenharmony_ci				"megaraid abort: %d[%d:%d], invalid state\n",
247962306a36Sopenharmony_ci				scb->sno, scb->dev_channel, scb->dev_target));
248062306a36Sopenharmony_ci				BUG();
248162306a36Sopenharmony_ci			}
248262306a36Sopenharmony_ci			else {
248362306a36Sopenharmony_ci				con_log(CL_ANN, (KERN_WARNING
248462306a36Sopenharmony_ci				"megaraid abort: %d[%d:%d], fw owner\n",
248562306a36Sopenharmony_ci				scb->sno, scb->dev_channel, scb->dev_target));
248662306a36Sopenharmony_ci			}
248762306a36Sopenharmony_ci		}
248862306a36Sopenharmony_ci	}
248962306a36Sopenharmony_ci	spin_unlock_irq(&adapter->lock);
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci	if (!found) {
249262306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING "megaraid abort: do now own\n"));
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_ci		// FIXME: Should there be a callback for this command?
249562306a36Sopenharmony_ci		return SUCCESS;
249662306a36Sopenharmony_ci	}
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	// We cannot actually abort a command owned by firmware, return
249962306a36Sopenharmony_ci	// failure and wait for reset. In host reset handler, we will find out
250062306a36Sopenharmony_ci	// if the HBA is still live
250162306a36Sopenharmony_ci	return FAILED;
250262306a36Sopenharmony_ci}
250362306a36Sopenharmony_ci
250462306a36Sopenharmony_ci/**
250562306a36Sopenharmony_ci * megaraid_reset_handler - device reset handler for mailbox based driver
250662306a36Sopenharmony_ci * @scp		: reference command
250762306a36Sopenharmony_ci *
250862306a36Sopenharmony_ci * Reset handler for the mailbox based controller. First try to find out if
250962306a36Sopenharmony_ci * the FW is still live, in which case the outstanding commands counter mut go
251062306a36Sopenharmony_ci * down to 0. If that happens, also issue the reservation reset command to
251162306a36Sopenharmony_ci * relinquish (possible) reservations on the logical drives connected to this
251262306a36Sopenharmony_ci * host.
251362306a36Sopenharmony_ci **/
251462306a36Sopenharmony_cistatic int
251562306a36Sopenharmony_cimegaraid_reset_handler(struct scsi_cmnd *scp)
251662306a36Sopenharmony_ci{
251762306a36Sopenharmony_ci	adapter_t	*adapter;
251862306a36Sopenharmony_ci	scb_t		*scb;
251962306a36Sopenharmony_ci	scb_t		*tmp;
252062306a36Sopenharmony_ci	mraid_device_t	*raid_dev;
252162306a36Sopenharmony_ci	unsigned long	flags;
252262306a36Sopenharmony_ci	uint8_t		raw_mbox[sizeof(mbox_t)];
252362306a36Sopenharmony_ci	int		rval;
252462306a36Sopenharmony_ci	int		recovery_window;
252562306a36Sopenharmony_ci	int		i;
252662306a36Sopenharmony_ci	uioc_t		*kioc;
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci	adapter		= SCP2ADAPTER(scp);
252962306a36Sopenharmony_ci	raid_dev	= ADAP2RAIDDEV(adapter);
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_ci	// return failure if adapter is not responding
253262306a36Sopenharmony_ci	if (raid_dev->hw_error) {
253362306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_NOTICE
253462306a36Sopenharmony_ci			"megaraid: hw error, cannot reset\n"));
253562306a36Sopenharmony_ci		return FAILED;
253662306a36Sopenharmony_ci	}
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	// Under exceptional conditions, FW can take up to 3 minutes to
253962306a36Sopenharmony_ci	// complete command processing. Wait for additional 2 minutes for the
254062306a36Sopenharmony_ci	// pending commands counter to go down to 0. If it doesn't, let the
254162306a36Sopenharmony_ci	// controller be marked offline
254262306a36Sopenharmony_ci	// Also, reset all the commands currently owned by the driver
254362306a36Sopenharmony_ci	spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags);
254462306a36Sopenharmony_ci	list_for_each_entry_safe(scb, tmp, &adapter->pend_list, list) {
254562306a36Sopenharmony_ci		list_del_init(&scb->list);	// from pending list
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_ci		if (scb->sno >= MBOX_MAX_SCSI_CMDS) {
254862306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_WARNING
254962306a36Sopenharmony_ci			"megaraid: IOCTL packet with %d[%d:%d] being reset\n",
255062306a36Sopenharmony_ci			scb->sno, scb->dev_channel, scb->dev_target));
255162306a36Sopenharmony_ci
255262306a36Sopenharmony_ci			scb->status = -1;
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci			kioc			= (uioc_t *)scb->gp;
255562306a36Sopenharmony_ci			kioc->status		= -EFAULT;
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci			megaraid_mbox_mm_done(adapter, scb);
255862306a36Sopenharmony_ci		} else {
255962306a36Sopenharmony_ci			if (scb->scp == scp) {	// Found command
256062306a36Sopenharmony_ci				con_log(CL_ANN, (KERN_WARNING
256162306a36Sopenharmony_ci					"megaraid: %d[%d:%d], reset from pending list\n",
256262306a36Sopenharmony_ci					scb->sno, scb->dev_channel, scb->dev_target));
256362306a36Sopenharmony_ci			} else {
256462306a36Sopenharmony_ci				con_log(CL_ANN, (KERN_WARNING
256562306a36Sopenharmony_ci				"megaraid: IO packet with %d[%d:%d] being reset\n",
256662306a36Sopenharmony_ci				scb->sno, scb->dev_channel, scb->dev_target));
256762306a36Sopenharmony_ci			}
256862306a36Sopenharmony_ci
256962306a36Sopenharmony_ci			scb->scp->result = (DID_RESET << 16);
257062306a36Sopenharmony_ci			scsi_done(scb->scp);
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci			megaraid_dealloc_scb(adapter, scb);
257362306a36Sopenharmony_ci		}
257462306a36Sopenharmony_ci	}
257562306a36Sopenharmony_ci	spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags);
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci	if (adapter->outstanding_cmds) {
257862306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_NOTICE
257962306a36Sopenharmony_ci			"megaraid: %d outstanding commands. Max wait %d sec\n",
258062306a36Sopenharmony_ci			adapter->outstanding_cmds,
258162306a36Sopenharmony_ci			(MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT)));
258262306a36Sopenharmony_ci	}
258362306a36Sopenharmony_ci
258462306a36Sopenharmony_ci	recovery_window = MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT;
258562306a36Sopenharmony_ci
258662306a36Sopenharmony_ci	for (i = 0; i < recovery_window; i++) {
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci		megaraid_ack_sequence(adapter);
258962306a36Sopenharmony_ci
259062306a36Sopenharmony_ci		// print a message once every 5 seconds only
259162306a36Sopenharmony_ci		if (!(i % 5)) {
259262306a36Sopenharmony_ci			con_log(CL_ANN, (
259362306a36Sopenharmony_ci			"megaraid mbox: Wait for %d commands to complete:%d\n",
259462306a36Sopenharmony_ci				adapter->outstanding_cmds,
259562306a36Sopenharmony_ci				(MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT) - i));
259662306a36Sopenharmony_ci		}
259762306a36Sopenharmony_ci
259862306a36Sopenharmony_ci		// bailout if no recovery happened in reset time
259962306a36Sopenharmony_ci		if (adapter->outstanding_cmds == 0) {
260062306a36Sopenharmony_ci			break;
260162306a36Sopenharmony_ci		}
260262306a36Sopenharmony_ci
260362306a36Sopenharmony_ci		msleep(1000);
260462306a36Sopenharmony_ci	}
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_ci	spin_lock(&adapter->lock);
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_ci	// If still outstanding commands, bail out
260962306a36Sopenharmony_ci	if (adapter->outstanding_cmds) {
261062306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
261162306a36Sopenharmony_ci			"megaraid mbox: critical hardware error!\n"));
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci		raid_dev->hw_error = 1;
261462306a36Sopenharmony_ci
261562306a36Sopenharmony_ci		rval = FAILED;
261662306a36Sopenharmony_ci		goto out;
261762306a36Sopenharmony_ci	}
261862306a36Sopenharmony_ci	else {
261962306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_NOTICE
262062306a36Sopenharmony_ci		"megaraid mbox: reset sequence completed successfully\n"));
262162306a36Sopenharmony_ci	}
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_ci	// If the controller supports clustering, reset reservations
262562306a36Sopenharmony_ci	if (!adapter->ha) {
262662306a36Sopenharmony_ci		rval = SUCCESS;
262762306a36Sopenharmony_ci		goto out;
262862306a36Sopenharmony_ci	}
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci	// clear reservations if any
263162306a36Sopenharmony_ci	raw_mbox[0] = CLUSTER_CMD;
263262306a36Sopenharmony_ci	raw_mbox[2] = RESET_RESERVATIONS;
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	rval = SUCCESS;
263562306a36Sopenharmony_ci	if (mbox_post_sync_cmd_fast(adapter, raw_mbox) == 0) {
263662306a36Sopenharmony_ci		con_log(CL_ANN,
263762306a36Sopenharmony_ci			(KERN_INFO "megaraid: reservation reset\n"));
263862306a36Sopenharmony_ci	}
263962306a36Sopenharmony_ci	else {
264062306a36Sopenharmony_ci		rval = FAILED;
264162306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
264262306a36Sopenharmony_ci				"megaraid: reservation reset failed\n"));
264362306a36Sopenharmony_ci	}
264462306a36Sopenharmony_ci
264562306a36Sopenharmony_ci out:
264662306a36Sopenharmony_ci	spin_unlock(&adapter->lock);
264762306a36Sopenharmony_ci	return rval;
264862306a36Sopenharmony_ci}
264962306a36Sopenharmony_ci
265062306a36Sopenharmony_ci/*
265162306a36Sopenharmony_ci * START: internal commands library
265262306a36Sopenharmony_ci *
265362306a36Sopenharmony_ci * This section of the driver has the common routine used by the driver and
265462306a36Sopenharmony_ci * also has all the FW routines
265562306a36Sopenharmony_ci */
265662306a36Sopenharmony_ci
265762306a36Sopenharmony_ci/**
265862306a36Sopenharmony_ci * mbox_post_sync_cmd() - blocking command to the mailbox based controllers
265962306a36Sopenharmony_ci * @adapter	: controller's soft state
266062306a36Sopenharmony_ci * @raw_mbox	: the mailbox
266162306a36Sopenharmony_ci *
266262306a36Sopenharmony_ci * Issue a scb in synchronous and non-interrupt mode for mailbox based
266362306a36Sopenharmony_ci * controllers.
266462306a36Sopenharmony_ci */
266562306a36Sopenharmony_cistatic int
266662306a36Sopenharmony_cimbox_post_sync_cmd(adapter_t *adapter, uint8_t raw_mbox[])
266762306a36Sopenharmony_ci{
266862306a36Sopenharmony_ci	mraid_device_t	*raid_dev = ADAP2RAIDDEV(adapter);
266962306a36Sopenharmony_ci	mbox_t		*mbox;
267062306a36Sopenharmony_ci	uint8_t		status;
267162306a36Sopenharmony_ci	int		i;
267262306a36Sopenharmony_ci
267362306a36Sopenharmony_ci	mbox	= raid_dev->mbox;
267462306a36Sopenharmony_ci
267562306a36Sopenharmony_ci	/*
267662306a36Sopenharmony_ci	 * Wait until mailbox is free
267762306a36Sopenharmony_ci	 */
267862306a36Sopenharmony_ci	if (megaraid_busywait_mbox(raid_dev) != 0)
267962306a36Sopenharmony_ci		goto blocked_mailbox;
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci	/*
268262306a36Sopenharmony_ci	 * Copy mailbox data into host structure
268362306a36Sopenharmony_ci	 */
268462306a36Sopenharmony_ci	memcpy((caddr_t)mbox, (caddr_t)raw_mbox, 16);
268562306a36Sopenharmony_ci	mbox->cmdid		= 0xFE;
268662306a36Sopenharmony_ci	mbox->busy		= 1;
268762306a36Sopenharmony_ci	mbox->poll		= 0;
268862306a36Sopenharmony_ci	mbox->ack		= 0;
268962306a36Sopenharmony_ci	mbox->numstatus		= 0xFF;
269062306a36Sopenharmony_ci	mbox->status		= 0xFF;
269162306a36Sopenharmony_ci
269262306a36Sopenharmony_ci	wmb();
269362306a36Sopenharmony_ci	WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1);
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_ci	// wait for maximum 1 second for status to post. If the status is not
269662306a36Sopenharmony_ci	// available within 1 second, assume FW is initializing and wait
269762306a36Sopenharmony_ci	// for an extended amount of time
269862306a36Sopenharmony_ci	if (mbox->numstatus == 0xFF) {	// status not yet available
269962306a36Sopenharmony_ci		udelay(25);
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci		for (i = 0; mbox->numstatus == 0xFF && i < 1000; i++) {
270262306a36Sopenharmony_ci			rmb();
270362306a36Sopenharmony_ci			msleep(1);
270462306a36Sopenharmony_ci		}
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci
270762306a36Sopenharmony_ci		if (i == 1000) {
270862306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_NOTICE
270962306a36Sopenharmony_ci				"megaraid mailbox: wait for FW to boot      "));
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_ci			for (i = 0; (mbox->numstatus == 0xFF) &&
271262306a36Sopenharmony_ci					(i < MBOX_RESET_WAIT); i++) {
271362306a36Sopenharmony_ci				rmb();
271462306a36Sopenharmony_ci				con_log(CL_ANN, ("\b\b\b\b\b[%03d]",
271562306a36Sopenharmony_ci							MBOX_RESET_WAIT - i));
271662306a36Sopenharmony_ci				msleep(1000);
271762306a36Sopenharmony_ci			}
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_ci			if (i == MBOX_RESET_WAIT) {
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_ci				con_log(CL_ANN, (
272262306a36Sopenharmony_ci				"\nmegaraid mailbox: status not available\n"));
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_ci				return -1;
272562306a36Sopenharmony_ci			}
272662306a36Sopenharmony_ci			con_log(CL_ANN, ("\b\b\b\b\b[ok] \n"));
272762306a36Sopenharmony_ci		}
272862306a36Sopenharmony_ci	}
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci	// wait for maximum 1 second for poll semaphore
273162306a36Sopenharmony_ci	if (mbox->poll != 0x77) {
273262306a36Sopenharmony_ci		udelay(25);
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_ci		for (i = 0; (mbox->poll != 0x77) && (i < 1000); i++) {
273562306a36Sopenharmony_ci			rmb();
273662306a36Sopenharmony_ci			msleep(1);
273762306a36Sopenharmony_ci		}
273862306a36Sopenharmony_ci
273962306a36Sopenharmony_ci		if (i == 1000) {
274062306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_WARNING
274162306a36Sopenharmony_ci			"megaraid mailbox: could not get poll semaphore\n"));
274262306a36Sopenharmony_ci			return -1;
274362306a36Sopenharmony_ci		}
274462306a36Sopenharmony_ci	}
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x2);
274762306a36Sopenharmony_ci	wmb();
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_ci	// wait for maximum 1 second for acknowledgement
275062306a36Sopenharmony_ci	if (RDINDOOR(raid_dev) & 0x2) {
275162306a36Sopenharmony_ci		udelay(25);
275262306a36Sopenharmony_ci
275362306a36Sopenharmony_ci		for (i = 0; (RDINDOOR(raid_dev) & 0x2) && (i < 1000); i++) {
275462306a36Sopenharmony_ci			rmb();
275562306a36Sopenharmony_ci			msleep(1);
275662306a36Sopenharmony_ci		}
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci		if (i == 1000) {
275962306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_WARNING
276062306a36Sopenharmony_ci				"megaraid mailbox: could not acknowledge\n"));
276162306a36Sopenharmony_ci			return -1;
276262306a36Sopenharmony_ci		}
276362306a36Sopenharmony_ci	}
276462306a36Sopenharmony_ci	mbox->poll	= 0;
276562306a36Sopenharmony_ci	mbox->ack	= 0x77;
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci	status = mbox->status;
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ci	// invalidate the completed command id array. After command
277062306a36Sopenharmony_ci	// completion, firmware would write the valid id.
277162306a36Sopenharmony_ci	mbox->numstatus	= 0xFF;
277262306a36Sopenharmony_ci	mbox->status	= 0xFF;
277362306a36Sopenharmony_ci	for (i = 0; i < MBOX_MAX_FIRMWARE_STATUS; i++) {
277462306a36Sopenharmony_ci		mbox->completed[i] = 0xFF;
277562306a36Sopenharmony_ci	}
277662306a36Sopenharmony_ci
277762306a36Sopenharmony_ci	return status;
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_ciblocked_mailbox:
278062306a36Sopenharmony_ci
278162306a36Sopenharmony_ci	con_log(CL_ANN, (KERN_WARNING "megaraid: blocked mailbox\n") );
278262306a36Sopenharmony_ci	return -1;
278362306a36Sopenharmony_ci}
278462306a36Sopenharmony_ci
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_ci/**
278762306a36Sopenharmony_ci * mbox_post_sync_cmd_fast - blocking command to the mailbox based controllers
278862306a36Sopenharmony_ci * @adapter	: controller's soft state
278962306a36Sopenharmony_ci * @raw_mbox	: the mailbox
279062306a36Sopenharmony_ci *
279162306a36Sopenharmony_ci * Issue a scb in synchronous and non-interrupt mode for mailbox based
279262306a36Sopenharmony_ci * controllers. This is a faster version of the synchronous command and
279362306a36Sopenharmony_ci * therefore can be called in interrupt-context as well.
279462306a36Sopenharmony_ci */
279562306a36Sopenharmony_cistatic int
279662306a36Sopenharmony_cimbox_post_sync_cmd_fast(adapter_t *adapter, uint8_t raw_mbox[])
279762306a36Sopenharmony_ci{
279862306a36Sopenharmony_ci	mraid_device_t	*raid_dev = ADAP2RAIDDEV(adapter);
279962306a36Sopenharmony_ci	mbox_t		*mbox;
280062306a36Sopenharmony_ci	long		i;
280162306a36Sopenharmony_ci
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_ci	mbox	= raid_dev->mbox;
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_ci	// return immediately if the mailbox is busy
280662306a36Sopenharmony_ci	if (mbox->busy) return -1;
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci	// Copy mailbox data into host structure
280962306a36Sopenharmony_ci	memcpy((caddr_t)mbox, (caddr_t)raw_mbox, 14);
281062306a36Sopenharmony_ci	mbox->cmdid		= 0xFE;
281162306a36Sopenharmony_ci	mbox->busy		= 1;
281262306a36Sopenharmony_ci	mbox->poll		= 0;
281362306a36Sopenharmony_ci	mbox->ack		= 0;
281462306a36Sopenharmony_ci	mbox->numstatus		= 0xFF;
281562306a36Sopenharmony_ci	mbox->status		= 0xFF;
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	wmb();
281862306a36Sopenharmony_ci	WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1);
281962306a36Sopenharmony_ci
282062306a36Sopenharmony_ci	for (i = 0; i < MBOX_SYNC_WAIT_CNT; i++) {
282162306a36Sopenharmony_ci		if (mbox->numstatus != 0xFF) break;
282262306a36Sopenharmony_ci		rmb();
282362306a36Sopenharmony_ci		udelay(MBOX_SYNC_DELAY_200);
282462306a36Sopenharmony_ci	}
282562306a36Sopenharmony_ci
282662306a36Sopenharmony_ci	if (i == MBOX_SYNC_WAIT_CNT) {
282762306a36Sopenharmony_ci		// We may need to re-calibrate the counter
282862306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_CRIT
282962306a36Sopenharmony_ci			"megaraid: fast sync command timed out\n"));
283062306a36Sopenharmony_ci	}
283162306a36Sopenharmony_ci
283262306a36Sopenharmony_ci	WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x2);
283362306a36Sopenharmony_ci	wmb();
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci	return mbox->status;
283662306a36Sopenharmony_ci}
283762306a36Sopenharmony_ci
283862306a36Sopenharmony_ci
283962306a36Sopenharmony_ci/**
284062306a36Sopenharmony_ci * megaraid_busywait_mbox() - Wait until the controller's mailbox is available
284162306a36Sopenharmony_ci * @raid_dev	: RAID device (HBA) soft state
284262306a36Sopenharmony_ci *
284362306a36Sopenharmony_ci * Wait until the controller's mailbox is available to accept more commands.
284462306a36Sopenharmony_ci * Wait for at most 1 second.
284562306a36Sopenharmony_ci */
284662306a36Sopenharmony_cistatic int
284762306a36Sopenharmony_cimegaraid_busywait_mbox(mraid_device_t *raid_dev)
284862306a36Sopenharmony_ci{
284962306a36Sopenharmony_ci	mbox_t	*mbox = raid_dev->mbox;
285062306a36Sopenharmony_ci	int	i = 0;
285162306a36Sopenharmony_ci
285262306a36Sopenharmony_ci	if (mbox->busy) {
285362306a36Sopenharmony_ci		udelay(25);
285462306a36Sopenharmony_ci		for (i = 0; mbox->busy && i < 1000; i++)
285562306a36Sopenharmony_ci			msleep(1);
285662306a36Sopenharmony_ci	}
285762306a36Sopenharmony_ci
285862306a36Sopenharmony_ci	if (i < 1000) return 0;
285962306a36Sopenharmony_ci	else return -1;
286062306a36Sopenharmony_ci}
286162306a36Sopenharmony_ci
286262306a36Sopenharmony_ci
286362306a36Sopenharmony_ci/**
286462306a36Sopenharmony_ci * megaraid_mbox_product_info - some static information about the controller
286562306a36Sopenharmony_ci * @adapter	: our soft state
286662306a36Sopenharmony_ci *
286762306a36Sopenharmony_ci * Issue commands to the controller to grab some parameters required by our
286862306a36Sopenharmony_ci * caller.
286962306a36Sopenharmony_ci */
287062306a36Sopenharmony_cistatic int
287162306a36Sopenharmony_cimegaraid_mbox_product_info(adapter_t *adapter)
287262306a36Sopenharmony_ci{
287362306a36Sopenharmony_ci	mraid_device_t		*raid_dev = ADAP2RAIDDEV(adapter);
287462306a36Sopenharmony_ci	mbox_t			*mbox;
287562306a36Sopenharmony_ci	uint8_t			raw_mbox[sizeof(mbox_t)];
287662306a36Sopenharmony_ci	mraid_pinfo_t		*pinfo;
287762306a36Sopenharmony_ci	dma_addr_t		pinfo_dma_h;
287862306a36Sopenharmony_ci	mraid_inquiry3_t	*mraid_inq3;
287962306a36Sopenharmony_ci	int			i;
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_ci
288262306a36Sopenharmony_ci	memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox));
288362306a36Sopenharmony_ci	mbox = (mbox_t *)raw_mbox;
288462306a36Sopenharmony_ci
288562306a36Sopenharmony_ci	/*
288662306a36Sopenharmony_ci	 * Issue an ENQUIRY3 command to find out certain adapter parameters,
288762306a36Sopenharmony_ci	 * e.g., max channels, max commands etc.
288862306a36Sopenharmony_ci	 */
288962306a36Sopenharmony_ci	pinfo = dma_alloc_coherent(&adapter->pdev->dev, sizeof(mraid_pinfo_t),
289062306a36Sopenharmony_ci				   &pinfo_dma_h, GFP_KERNEL);
289162306a36Sopenharmony_ci	if (pinfo == NULL) {
289262306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
289362306a36Sopenharmony_ci			"megaraid: out of memory, %s %d\n", __func__,
289462306a36Sopenharmony_ci			__LINE__));
289562306a36Sopenharmony_ci
289662306a36Sopenharmony_ci		return -1;
289762306a36Sopenharmony_ci	}
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_ci	mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h;
290062306a36Sopenharmony_ci	memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE);
290162306a36Sopenharmony_ci
290262306a36Sopenharmony_ci	raw_mbox[0] = FC_NEW_CONFIG;
290362306a36Sopenharmony_ci	raw_mbox[2] = NC_SUBOP_ENQUIRY3;
290462306a36Sopenharmony_ci	raw_mbox[3] = ENQ3_GET_SOLICITED_FULL;
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci	// Issue the command
290762306a36Sopenharmony_ci	if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) {
290862306a36Sopenharmony_ci
290962306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING "megaraid: Inquiry3 failed\n"));
291062306a36Sopenharmony_ci
291162306a36Sopenharmony_ci		dma_free_coherent(&adapter->pdev->dev, sizeof(mraid_pinfo_t),
291262306a36Sopenharmony_ci			pinfo, pinfo_dma_h);
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_ci		return -1;
291562306a36Sopenharmony_ci	}
291662306a36Sopenharmony_ci
291762306a36Sopenharmony_ci	/*
291862306a36Sopenharmony_ci	 * Collect information about state of each physical drive
291962306a36Sopenharmony_ci	 * attached to the controller. We will expose all the disks
292062306a36Sopenharmony_ci	 * which are not part of RAID
292162306a36Sopenharmony_ci	 */
292262306a36Sopenharmony_ci	mraid_inq3 = (mraid_inquiry3_t *)adapter->ibuf;
292362306a36Sopenharmony_ci	for (i = 0; i < MBOX_MAX_PHYSICAL_DRIVES; i++) {
292462306a36Sopenharmony_ci		raid_dev->pdrv_state[i] = mraid_inq3->pdrv_state[i];
292562306a36Sopenharmony_ci	}
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_ci	/*
292862306a36Sopenharmony_ci	 * Get product info for information like number of channels,
292962306a36Sopenharmony_ci	 * maximum commands supported.
293062306a36Sopenharmony_ci	 */
293162306a36Sopenharmony_ci	memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox));
293262306a36Sopenharmony_ci	mbox->xferaddr = (uint32_t)pinfo_dma_h;
293362306a36Sopenharmony_ci
293462306a36Sopenharmony_ci	raw_mbox[0] = FC_NEW_CONFIG;
293562306a36Sopenharmony_ci	raw_mbox[2] = NC_SUBOP_PRODUCT_INFO;
293662306a36Sopenharmony_ci
293762306a36Sopenharmony_ci	if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) {
293862306a36Sopenharmony_ci
293962306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
294062306a36Sopenharmony_ci			"megaraid: product info failed\n"));
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_ci		dma_free_coherent(&adapter->pdev->dev, sizeof(mraid_pinfo_t),
294362306a36Sopenharmony_ci			pinfo, pinfo_dma_h);
294462306a36Sopenharmony_ci
294562306a36Sopenharmony_ci		return -1;
294662306a36Sopenharmony_ci	}
294762306a36Sopenharmony_ci
294862306a36Sopenharmony_ci	/*
294962306a36Sopenharmony_ci	 * Setup some parameters for host, as required by our caller
295062306a36Sopenharmony_ci	 */
295162306a36Sopenharmony_ci	adapter->max_channel = pinfo->nchannels;
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci	/*
295462306a36Sopenharmony_ci	 * we will export all the logical drives on a single channel.
295562306a36Sopenharmony_ci	 * Add 1 since inquires do not come for inititor ID
295662306a36Sopenharmony_ci	 */
295762306a36Sopenharmony_ci	adapter->max_target	= MAX_LOGICAL_DRIVES_40LD + 1;
295862306a36Sopenharmony_ci	adapter->max_lun	= 8;	// up to 8 LUNs for non-disk devices
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_ci	/*
296162306a36Sopenharmony_ci	 * These are the maximum outstanding commands for the scsi-layer
296262306a36Sopenharmony_ci	 */
296362306a36Sopenharmony_ci	adapter->max_cmds	= MBOX_MAX_SCSI_CMDS;
296462306a36Sopenharmony_ci
296562306a36Sopenharmony_ci	memset(adapter->fw_version, 0, VERSION_SIZE);
296662306a36Sopenharmony_ci	memset(adapter->bios_version, 0, VERSION_SIZE);
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ci	memcpy(adapter->fw_version, pinfo->fw_version, 4);
296962306a36Sopenharmony_ci	adapter->fw_version[4] = 0;
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_ci	memcpy(adapter->bios_version, pinfo->bios_version, 4);
297262306a36Sopenharmony_ci	adapter->bios_version[4] = 0;
297362306a36Sopenharmony_ci
297462306a36Sopenharmony_ci	con_log(CL_ANN, (KERN_NOTICE
297562306a36Sopenharmony_ci		"megaraid: fw version:[%s] bios version:[%s]\n",
297662306a36Sopenharmony_ci		adapter->fw_version, adapter->bios_version));
297762306a36Sopenharmony_ci
297862306a36Sopenharmony_ci	dma_free_coherent(&adapter->pdev->dev, sizeof(mraid_pinfo_t), pinfo,
297962306a36Sopenharmony_ci			pinfo_dma_h);
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_ci	return 0;
298262306a36Sopenharmony_ci}
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_ci
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_ci/**
298762306a36Sopenharmony_ci * megaraid_mbox_extended_cdb - check for support for extended CDBs
298862306a36Sopenharmony_ci * @adapter	: soft state for the controller
298962306a36Sopenharmony_ci *
299062306a36Sopenharmony_ci * This routine check whether the controller in question supports extended
299162306a36Sopenharmony_ci * ( > 10 bytes ) CDBs.
299262306a36Sopenharmony_ci */
299362306a36Sopenharmony_cistatic int
299462306a36Sopenharmony_cimegaraid_mbox_extended_cdb(adapter_t *adapter)
299562306a36Sopenharmony_ci{
299662306a36Sopenharmony_ci	mbox_t		*mbox;
299762306a36Sopenharmony_ci	uint8_t		raw_mbox[sizeof(mbox_t)];
299862306a36Sopenharmony_ci	int		rval;
299962306a36Sopenharmony_ci
300062306a36Sopenharmony_ci	mbox = (mbox_t *)raw_mbox;
300162306a36Sopenharmony_ci
300262306a36Sopenharmony_ci	memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox));
300362306a36Sopenharmony_ci	mbox->xferaddr	= (uint32_t)adapter->ibuf_dma_h;
300462306a36Sopenharmony_ci
300562306a36Sopenharmony_ci	memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE);
300662306a36Sopenharmony_ci
300762306a36Sopenharmony_ci	raw_mbox[0] = MAIN_MISC_OPCODE;
300862306a36Sopenharmony_ci	raw_mbox[2] = SUPPORT_EXT_CDB;
300962306a36Sopenharmony_ci
301062306a36Sopenharmony_ci	/*
301162306a36Sopenharmony_ci	 * Issue the command
301262306a36Sopenharmony_ci	 */
301362306a36Sopenharmony_ci	rval = 0;
301462306a36Sopenharmony_ci	if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) {
301562306a36Sopenharmony_ci		rval = -1;
301662306a36Sopenharmony_ci	}
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_ci	return rval;
301962306a36Sopenharmony_ci}
302062306a36Sopenharmony_ci
302162306a36Sopenharmony_ci
302262306a36Sopenharmony_ci/**
302362306a36Sopenharmony_ci * megaraid_mbox_support_ha - Do we support clustering
302462306a36Sopenharmony_ci * @adapter	: soft state for the controller
302562306a36Sopenharmony_ci * @init_id	: ID of the initiator
302662306a36Sopenharmony_ci *
302762306a36Sopenharmony_ci * Determine if the firmware supports clustering and the ID of the initiator.
302862306a36Sopenharmony_ci */
302962306a36Sopenharmony_cistatic int
303062306a36Sopenharmony_cimegaraid_mbox_support_ha(adapter_t *adapter, uint16_t *init_id)
303162306a36Sopenharmony_ci{
303262306a36Sopenharmony_ci	mbox_t		*mbox;
303362306a36Sopenharmony_ci	uint8_t		raw_mbox[sizeof(mbox_t)];
303462306a36Sopenharmony_ci	int		rval;
303562306a36Sopenharmony_ci
303662306a36Sopenharmony_ci
303762306a36Sopenharmony_ci	mbox = (mbox_t *)raw_mbox;
303862306a36Sopenharmony_ci
303962306a36Sopenharmony_ci	memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox));
304062306a36Sopenharmony_ci
304162306a36Sopenharmony_ci	mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h;
304262306a36Sopenharmony_ci
304362306a36Sopenharmony_ci	memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE);
304462306a36Sopenharmony_ci
304562306a36Sopenharmony_ci	raw_mbox[0] = GET_TARGET_ID;
304662306a36Sopenharmony_ci
304762306a36Sopenharmony_ci	// Issue the command
304862306a36Sopenharmony_ci	*init_id = 7;
304962306a36Sopenharmony_ci	rval =  -1;
305062306a36Sopenharmony_ci	if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) {
305162306a36Sopenharmony_ci
305262306a36Sopenharmony_ci		*init_id = *(uint8_t *)adapter->ibuf;
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_INFO
305562306a36Sopenharmony_ci			"megaraid: cluster firmware, initiator ID: %d\n",
305662306a36Sopenharmony_ci			*init_id));
305762306a36Sopenharmony_ci
305862306a36Sopenharmony_ci		rval =  0;
305962306a36Sopenharmony_ci	}
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ci	return rval;
306262306a36Sopenharmony_ci}
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci
306562306a36Sopenharmony_ci/**
306662306a36Sopenharmony_ci * megaraid_mbox_support_random_del - Do we support random deletion
306762306a36Sopenharmony_ci * @adapter	: soft state for the controller
306862306a36Sopenharmony_ci *
306962306a36Sopenharmony_ci * Determine if the firmware supports random deletion.
307062306a36Sopenharmony_ci * Return:	1 is operation supported, 0 otherwise
307162306a36Sopenharmony_ci */
307262306a36Sopenharmony_cistatic int
307362306a36Sopenharmony_cimegaraid_mbox_support_random_del(adapter_t *adapter)
307462306a36Sopenharmony_ci{
307562306a36Sopenharmony_ci	uint8_t		raw_mbox[sizeof(mbox_t)];
307662306a36Sopenharmony_ci	int		rval;
307762306a36Sopenharmony_ci
307862306a36Sopenharmony_ci	/*
307962306a36Sopenharmony_ci	 * Newer firmware on Dell CERC expect a different
308062306a36Sopenharmony_ci	 * random deletion handling, so disable it.
308162306a36Sopenharmony_ci	 */
308262306a36Sopenharmony_ci	if (adapter->pdev->vendor == PCI_VENDOR_ID_AMI &&
308362306a36Sopenharmony_ci	    adapter->pdev->device == PCI_DEVICE_ID_AMI_MEGARAID3 &&
308462306a36Sopenharmony_ci	    adapter->pdev->subsystem_vendor == PCI_VENDOR_ID_DELL &&
308562306a36Sopenharmony_ci	    adapter->pdev->subsystem_device == PCI_SUBSYS_ID_CERC_ATA100_4CH &&
308662306a36Sopenharmony_ci	    (adapter->fw_version[0] > '6' ||
308762306a36Sopenharmony_ci	     (adapter->fw_version[0] == '6' &&
308862306a36Sopenharmony_ci	      adapter->fw_version[2] > '6') ||
308962306a36Sopenharmony_ci	     (adapter->fw_version[0] == '6'
309062306a36Sopenharmony_ci	      && adapter->fw_version[2] == '6'
309162306a36Sopenharmony_ci	      && adapter->fw_version[3] > '1'))) {
309262306a36Sopenharmony_ci		con_log(CL_DLEVEL1, ("megaraid: disable random deletion\n"));
309362306a36Sopenharmony_ci		return 0;
309462306a36Sopenharmony_ci	}
309562306a36Sopenharmony_ci
309662306a36Sopenharmony_ci	memset((caddr_t)raw_mbox, 0, sizeof(mbox_t));
309762306a36Sopenharmony_ci
309862306a36Sopenharmony_ci	raw_mbox[0] = FC_DEL_LOGDRV;
309962306a36Sopenharmony_ci	raw_mbox[2] = OP_SUP_DEL_LOGDRV;
310062306a36Sopenharmony_ci
310162306a36Sopenharmony_ci	// Issue the command
310262306a36Sopenharmony_ci	rval = 0;
310362306a36Sopenharmony_ci	if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) {
310462306a36Sopenharmony_ci
310562306a36Sopenharmony_ci		con_log(CL_DLEVEL1, ("megaraid: supports random deletion\n"));
310662306a36Sopenharmony_ci
310762306a36Sopenharmony_ci		rval =  1;
310862306a36Sopenharmony_ci	}
310962306a36Sopenharmony_ci
311062306a36Sopenharmony_ci	return rval;
311162306a36Sopenharmony_ci}
311262306a36Sopenharmony_ci
311362306a36Sopenharmony_ci
311462306a36Sopenharmony_ci/**
311562306a36Sopenharmony_ci * megaraid_mbox_get_max_sg - maximum sg elements supported by the firmware
311662306a36Sopenharmony_ci * @adapter	: soft state for the controller
311762306a36Sopenharmony_ci *
311862306a36Sopenharmony_ci * Find out the maximum number of scatter-gather elements supported by the
311962306a36Sopenharmony_ci * firmware.
312062306a36Sopenharmony_ci */
312162306a36Sopenharmony_cistatic int
312262306a36Sopenharmony_cimegaraid_mbox_get_max_sg(adapter_t *adapter)
312362306a36Sopenharmony_ci{
312462306a36Sopenharmony_ci	mbox_t		*mbox;
312562306a36Sopenharmony_ci	uint8_t		raw_mbox[sizeof(mbox_t)];
312662306a36Sopenharmony_ci	int		nsg;
312762306a36Sopenharmony_ci
312862306a36Sopenharmony_ci
312962306a36Sopenharmony_ci	mbox = (mbox_t *)raw_mbox;
313062306a36Sopenharmony_ci
313162306a36Sopenharmony_ci	memset((caddr_t)raw_mbox, 0, sizeof(mbox_t));
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_ci	mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h;
313462306a36Sopenharmony_ci
313562306a36Sopenharmony_ci	memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE);
313662306a36Sopenharmony_ci
313762306a36Sopenharmony_ci	raw_mbox[0] = MAIN_MISC_OPCODE;
313862306a36Sopenharmony_ci	raw_mbox[2] = GET_MAX_SG_SUPPORT;
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_ci	// Issue the command
314162306a36Sopenharmony_ci	if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) {
314262306a36Sopenharmony_ci		nsg =  *(uint8_t *)adapter->ibuf;
314362306a36Sopenharmony_ci	}
314462306a36Sopenharmony_ci	else {
314562306a36Sopenharmony_ci		nsg =  MBOX_DEFAULT_SG_SIZE;
314662306a36Sopenharmony_ci	}
314762306a36Sopenharmony_ci
314862306a36Sopenharmony_ci	if (nsg > MBOX_MAX_SG_SIZE) nsg = MBOX_MAX_SG_SIZE;
314962306a36Sopenharmony_ci
315062306a36Sopenharmony_ci	return nsg;
315162306a36Sopenharmony_ci}
315262306a36Sopenharmony_ci
315362306a36Sopenharmony_ci
315462306a36Sopenharmony_ci/**
315562306a36Sopenharmony_ci * megaraid_mbox_enum_raid_scsi - enumerate the RAID and SCSI channels
315662306a36Sopenharmony_ci * @adapter	: soft state for the controller
315762306a36Sopenharmony_ci *
315862306a36Sopenharmony_ci * Enumerate the RAID and SCSI channels for ROMB platforms so that channels
315962306a36Sopenharmony_ci * can be exported as regular SCSI channels.
316062306a36Sopenharmony_ci */
316162306a36Sopenharmony_cistatic void
316262306a36Sopenharmony_cimegaraid_mbox_enum_raid_scsi(adapter_t *adapter)
316362306a36Sopenharmony_ci{
316462306a36Sopenharmony_ci	mraid_device_t	*raid_dev = ADAP2RAIDDEV(adapter);
316562306a36Sopenharmony_ci	mbox_t		*mbox;
316662306a36Sopenharmony_ci	uint8_t		raw_mbox[sizeof(mbox_t)];
316762306a36Sopenharmony_ci
316862306a36Sopenharmony_ci
316962306a36Sopenharmony_ci	mbox = (mbox_t *)raw_mbox;
317062306a36Sopenharmony_ci
317162306a36Sopenharmony_ci	memset((caddr_t)raw_mbox, 0, sizeof(mbox_t));
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_ci	mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h;
317462306a36Sopenharmony_ci
317562306a36Sopenharmony_ci	memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE);
317662306a36Sopenharmony_ci
317762306a36Sopenharmony_ci	raw_mbox[0] = CHNL_CLASS;
317862306a36Sopenharmony_ci	raw_mbox[2] = GET_CHNL_CLASS;
317962306a36Sopenharmony_ci
318062306a36Sopenharmony_ci	// Issue the command. If the command fails, all channels are RAID
318162306a36Sopenharmony_ci	// channels
318262306a36Sopenharmony_ci	raid_dev->channel_class = 0xFF;
318362306a36Sopenharmony_ci	if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) {
318462306a36Sopenharmony_ci		raid_dev->channel_class =  *(uint8_t *)adapter->ibuf;
318562306a36Sopenharmony_ci	}
318662306a36Sopenharmony_ci
318762306a36Sopenharmony_ci	return;
318862306a36Sopenharmony_ci}
318962306a36Sopenharmony_ci
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci/**
319262306a36Sopenharmony_ci * megaraid_mbox_flush_cache - flush adapter and disks cache
319362306a36Sopenharmony_ci * @adapter		: soft state for the controller
319462306a36Sopenharmony_ci *
319562306a36Sopenharmony_ci * Flush adapter cache followed by disks cache.
319662306a36Sopenharmony_ci */
319762306a36Sopenharmony_cistatic void
319862306a36Sopenharmony_cimegaraid_mbox_flush_cache(adapter_t *adapter)
319962306a36Sopenharmony_ci{
320062306a36Sopenharmony_ci	uint8_t	raw_mbox[sizeof(mbox_t)];
320162306a36Sopenharmony_ci
320262306a36Sopenharmony_ci	memset((caddr_t)raw_mbox, 0, sizeof(mbox_t));
320362306a36Sopenharmony_ci
320462306a36Sopenharmony_ci	raw_mbox[0] = FLUSH_ADAPTER;
320562306a36Sopenharmony_ci
320662306a36Sopenharmony_ci	if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) {
320762306a36Sopenharmony_ci		con_log(CL_ANN, ("megaraid: flush adapter failed\n"));
320862306a36Sopenharmony_ci	}
320962306a36Sopenharmony_ci
321062306a36Sopenharmony_ci	raw_mbox[0] = FLUSH_SYSTEM;
321162306a36Sopenharmony_ci
321262306a36Sopenharmony_ci	if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) {
321362306a36Sopenharmony_ci		con_log(CL_ANN, ("megaraid: flush disks cache failed\n"));
321462306a36Sopenharmony_ci	}
321562306a36Sopenharmony_ci
321662306a36Sopenharmony_ci	return;
321762306a36Sopenharmony_ci}
321862306a36Sopenharmony_ci
321962306a36Sopenharmony_ci
322062306a36Sopenharmony_ci/**
322162306a36Sopenharmony_ci * megaraid_mbox_fire_sync_cmd - fire the sync cmd
322262306a36Sopenharmony_ci * @adapter		: soft state for the controller
322362306a36Sopenharmony_ci *
322462306a36Sopenharmony_ci * Clears the pending cmds in FW and reinits its RAID structs.
322562306a36Sopenharmony_ci */
322662306a36Sopenharmony_cistatic int
322762306a36Sopenharmony_cimegaraid_mbox_fire_sync_cmd(adapter_t *adapter)
322862306a36Sopenharmony_ci{
322962306a36Sopenharmony_ci	mbox_t	*mbox;
323062306a36Sopenharmony_ci	uint8_t	raw_mbox[sizeof(mbox_t)];
323162306a36Sopenharmony_ci	mraid_device_t	*raid_dev = ADAP2RAIDDEV(adapter);
323262306a36Sopenharmony_ci	int	status = 0;
323362306a36Sopenharmony_ci	int i;
323462306a36Sopenharmony_ci	uint32_t dword;
323562306a36Sopenharmony_ci
323662306a36Sopenharmony_ci	memset((caddr_t)raw_mbox, 0, sizeof(mbox_t));
323762306a36Sopenharmony_ci
323862306a36Sopenharmony_ci	raw_mbox[0] = 0xFF;
323962306a36Sopenharmony_ci
324062306a36Sopenharmony_ci	mbox	= raid_dev->mbox;
324162306a36Sopenharmony_ci
324262306a36Sopenharmony_ci	/* Wait until mailbox is free */
324362306a36Sopenharmony_ci	if (megaraid_busywait_mbox(raid_dev) != 0) {
324462306a36Sopenharmony_ci		status = 1;
324562306a36Sopenharmony_ci		goto blocked_mailbox;
324662306a36Sopenharmony_ci	}
324762306a36Sopenharmony_ci
324862306a36Sopenharmony_ci	/* Copy mailbox data into host structure */
324962306a36Sopenharmony_ci	memcpy((caddr_t)mbox, (caddr_t)raw_mbox, 16);
325062306a36Sopenharmony_ci	mbox->cmdid		= 0xFE;
325162306a36Sopenharmony_ci	mbox->busy		= 1;
325262306a36Sopenharmony_ci	mbox->poll		= 0;
325362306a36Sopenharmony_ci	mbox->ack		= 0;
325462306a36Sopenharmony_ci	mbox->numstatus		= 0;
325562306a36Sopenharmony_ci	mbox->status		= 0;
325662306a36Sopenharmony_ci
325762306a36Sopenharmony_ci	wmb();
325862306a36Sopenharmony_ci	WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1);
325962306a36Sopenharmony_ci
326062306a36Sopenharmony_ci	/* Wait for maximum 1 min for status to post.
326162306a36Sopenharmony_ci	 * If the Firmware SUPPORTS the ABOVE COMMAND,
326262306a36Sopenharmony_ci	 * mbox->cmd will be set to 0
326362306a36Sopenharmony_ci	 * else
326462306a36Sopenharmony_ci	 * the firmware will reject the command with
326562306a36Sopenharmony_ci	 * mbox->numstatus set to 1
326662306a36Sopenharmony_ci	 */
326762306a36Sopenharmony_ci
326862306a36Sopenharmony_ci	i = 0;
326962306a36Sopenharmony_ci	status = 0;
327062306a36Sopenharmony_ci	while (!mbox->numstatus && mbox->cmd == 0xFF) {
327162306a36Sopenharmony_ci		rmb();
327262306a36Sopenharmony_ci		msleep(1);
327362306a36Sopenharmony_ci		i++;
327462306a36Sopenharmony_ci		if (i > 1000 * 60) {
327562306a36Sopenharmony_ci			status = 1;
327662306a36Sopenharmony_ci			break;
327762306a36Sopenharmony_ci		}
327862306a36Sopenharmony_ci	}
327962306a36Sopenharmony_ci	if (mbox->numstatus == 1)
328062306a36Sopenharmony_ci		status = 1; /*cmd not supported*/
328162306a36Sopenharmony_ci
328262306a36Sopenharmony_ci	/* Check for interrupt line */
328362306a36Sopenharmony_ci	dword = RDOUTDOOR(raid_dev);
328462306a36Sopenharmony_ci	WROUTDOOR(raid_dev, dword);
328562306a36Sopenharmony_ci	WRINDOOR(raid_dev,2);
328662306a36Sopenharmony_ci
328762306a36Sopenharmony_ci	return status;
328862306a36Sopenharmony_ci
328962306a36Sopenharmony_ciblocked_mailbox:
329062306a36Sopenharmony_ci	con_log(CL_ANN, (KERN_WARNING "megaraid: blocked mailbox\n"));
329162306a36Sopenharmony_ci	return status;
329262306a36Sopenharmony_ci}
329362306a36Sopenharmony_ci
329462306a36Sopenharmony_ci/**
329562306a36Sopenharmony_ci * megaraid_mbox_display_scb - display SCB information, mostly debug purposes
329662306a36Sopenharmony_ci * @adapter		: controller's soft state
329762306a36Sopenharmony_ci * @scb			: SCB to be displayed
329862306a36Sopenharmony_ci *
329962306a36Sopenharmony_ci * Diplay information about the given SCB iff the current debug level is
330062306a36Sopenharmony_ci * verbose.
330162306a36Sopenharmony_ci */
330262306a36Sopenharmony_cistatic void
330362306a36Sopenharmony_cimegaraid_mbox_display_scb(adapter_t *adapter, scb_t *scb)
330462306a36Sopenharmony_ci{
330562306a36Sopenharmony_ci	mbox_ccb_t		*ccb;
330662306a36Sopenharmony_ci	struct scsi_cmnd	*scp;
330762306a36Sopenharmony_ci	mbox_t			*mbox;
330862306a36Sopenharmony_ci	int			level;
330962306a36Sopenharmony_ci	int			i;
331062306a36Sopenharmony_ci
331162306a36Sopenharmony_ci
331262306a36Sopenharmony_ci	ccb	= (mbox_ccb_t *)scb->ccb;
331362306a36Sopenharmony_ci	scp	= scb->scp;
331462306a36Sopenharmony_ci	mbox	= ccb->mbox;
331562306a36Sopenharmony_ci
331662306a36Sopenharmony_ci	level = CL_DLEVEL3;
331762306a36Sopenharmony_ci
331862306a36Sopenharmony_ci	con_log(level, (KERN_NOTICE
331962306a36Sopenharmony_ci		"megaraid mailbox: status:%#x cmd:%#x id:%#x ", scb->status,
332062306a36Sopenharmony_ci		mbox->cmd, scb->sno));
332162306a36Sopenharmony_ci
332262306a36Sopenharmony_ci	con_log(level, ("sec:%#x lba:%#x addr:%#x ld:%d sg:%d\n",
332362306a36Sopenharmony_ci		mbox->numsectors, mbox->lba, mbox->xferaddr, mbox->logdrv,
332462306a36Sopenharmony_ci		mbox->numsge));
332562306a36Sopenharmony_ci
332662306a36Sopenharmony_ci	if (!scp) return;
332762306a36Sopenharmony_ci
332862306a36Sopenharmony_ci	con_log(level, (KERN_NOTICE "scsi cmnd: "));
332962306a36Sopenharmony_ci
333062306a36Sopenharmony_ci	for (i = 0; i < scp->cmd_len; i++) {
333162306a36Sopenharmony_ci		con_log(level, ("%#2.02x ", scp->cmnd[i]));
333262306a36Sopenharmony_ci	}
333362306a36Sopenharmony_ci
333462306a36Sopenharmony_ci	con_log(level, ("\n"));
333562306a36Sopenharmony_ci
333662306a36Sopenharmony_ci	return;
333762306a36Sopenharmony_ci}
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_ci
334062306a36Sopenharmony_ci/**
334162306a36Sopenharmony_ci * megaraid_mbox_setup_device_map - manage device ids
334262306a36Sopenharmony_ci * @adapter	: Driver's soft state
334362306a36Sopenharmony_ci *
334462306a36Sopenharmony_ci * Manage the device ids to have an appropriate mapping between the kernel
334562306a36Sopenharmony_ci * scsi addresses and megaraid scsi and logical drive addresses. We export
334662306a36Sopenharmony_ci * scsi devices on their actual addresses, whereas the logical drives are
334762306a36Sopenharmony_ci * exported on a virtual scsi channel.
334862306a36Sopenharmony_ci */
334962306a36Sopenharmony_cistatic void
335062306a36Sopenharmony_cimegaraid_mbox_setup_device_map(adapter_t *adapter)
335162306a36Sopenharmony_ci{
335262306a36Sopenharmony_ci	uint8_t		c;
335362306a36Sopenharmony_ci	uint8_t		t;
335462306a36Sopenharmony_ci
335562306a36Sopenharmony_ci	/*
335662306a36Sopenharmony_ci	 * First fill the values on the logical drive channel
335762306a36Sopenharmony_ci	 */
335862306a36Sopenharmony_ci	for (t = 0; t < LSI_MAX_LOGICAL_DRIVES_64LD; t++)
335962306a36Sopenharmony_ci		adapter->device_ids[adapter->max_channel][t] =
336062306a36Sopenharmony_ci			(t < adapter->init_id) ?  t : t - 1;
336162306a36Sopenharmony_ci
336262306a36Sopenharmony_ci	adapter->device_ids[adapter->max_channel][adapter->init_id] = 0xFF;
336362306a36Sopenharmony_ci
336462306a36Sopenharmony_ci	/*
336562306a36Sopenharmony_ci	 * Fill the values on the physical devices channels
336662306a36Sopenharmony_ci	 */
336762306a36Sopenharmony_ci	for (c = 0; c < adapter->max_channel; c++)
336862306a36Sopenharmony_ci		for (t = 0; t < LSI_MAX_LOGICAL_DRIVES_64LD; t++)
336962306a36Sopenharmony_ci			adapter->device_ids[c][t] = (c << 8) | t;
337062306a36Sopenharmony_ci}
337162306a36Sopenharmony_ci
337262306a36Sopenharmony_ci
337362306a36Sopenharmony_ci/*
337462306a36Sopenharmony_ci * END: internal commands library
337562306a36Sopenharmony_ci */
337662306a36Sopenharmony_ci
337762306a36Sopenharmony_ci/*
337862306a36Sopenharmony_ci * START: Interface for the common management module
337962306a36Sopenharmony_ci *
338062306a36Sopenharmony_ci * This is the module, which interfaces with the common management module to
338162306a36Sopenharmony_ci * provide support for ioctl and sysfs
338262306a36Sopenharmony_ci */
338362306a36Sopenharmony_ci
338462306a36Sopenharmony_ci/**
338562306a36Sopenharmony_ci * megaraid_cmm_register - register with the management module
338662306a36Sopenharmony_ci * @adapter		: HBA soft state
338762306a36Sopenharmony_ci *
338862306a36Sopenharmony_ci * Register with the management module, which allows applications to issue
338962306a36Sopenharmony_ci * ioctl calls to the drivers. This interface is used by the management module
339062306a36Sopenharmony_ci * to setup sysfs support as well.
339162306a36Sopenharmony_ci */
339262306a36Sopenharmony_cistatic int
339362306a36Sopenharmony_cimegaraid_cmm_register(adapter_t *adapter)
339462306a36Sopenharmony_ci{
339562306a36Sopenharmony_ci	mraid_device_t	*raid_dev = ADAP2RAIDDEV(adapter);
339662306a36Sopenharmony_ci	mraid_mmadp_t	adp;
339762306a36Sopenharmony_ci	scb_t		*scb;
339862306a36Sopenharmony_ci	mbox_ccb_t	*ccb;
339962306a36Sopenharmony_ci	int		rval;
340062306a36Sopenharmony_ci	int		i;
340162306a36Sopenharmony_ci
340262306a36Sopenharmony_ci	// Allocate memory for the base list of scb for management module.
340362306a36Sopenharmony_ci	adapter->uscb_list = kcalloc(MBOX_MAX_USER_CMDS, sizeof(scb_t), GFP_KERNEL);
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_ci	if (adapter->uscb_list == NULL) {
340662306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
340762306a36Sopenharmony_ci			"megaraid: out of memory, %s %d\n", __func__,
340862306a36Sopenharmony_ci			__LINE__));
340962306a36Sopenharmony_ci		return -1;
341062306a36Sopenharmony_ci	}
341162306a36Sopenharmony_ci
341262306a36Sopenharmony_ci
341362306a36Sopenharmony_ci	// Initialize the synchronization parameters for resources for
341462306a36Sopenharmony_ci	// commands for management module
341562306a36Sopenharmony_ci	INIT_LIST_HEAD(&adapter->uscb_pool);
341662306a36Sopenharmony_ci
341762306a36Sopenharmony_ci	spin_lock_init(USER_FREE_LIST_LOCK(adapter));
341862306a36Sopenharmony_ci
341962306a36Sopenharmony_ci
342062306a36Sopenharmony_ci
342162306a36Sopenharmony_ci	// link all the packets. Note, CCB for commands, coming from the
342262306a36Sopenharmony_ci	// commom management module, mailbox physical address are already
342362306a36Sopenharmony_ci	// setup by it. We just need placeholder for that in our local command
342462306a36Sopenharmony_ci	// control blocks
342562306a36Sopenharmony_ci	for (i = 0; i < MBOX_MAX_USER_CMDS; i++) {
342662306a36Sopenharmony_ci
342762306a36Sopenharmony_ci		scb			= adapter->uscb_list + i;
342862306a36Sopenharmony_ci		ccb			= raid_dev->uccb_list + i;
342962306a36Sopenharmony_ci
343062306a36Sopenharmony_ci		scb->ccb		= (caddr_t)ccb;
343162306a36Sopenharmony_ci		ccb->mbox64		= raid_dev->umbox64 + i;
343262306a36Sopenharmony_ci		ccb->mbox		= &ccb->mbox64->mbox32;
343362306a36Sopenharmony_ci		ccb->raw_mbox		= (uint8_t *)ccb->mbox;
343462306a36Sopenharmony_ci
343562306a36Sopenharmony_ci		scb->gp			= 0;
343662306a36Sopenharmony_ci
343762306a36Sopenharmony_ci		// COMMAND ID 0 - (MBOX_MAX_SCSI_CMDS-1) ARE RESERVED FOR
343862306a36Sopenharmony_ci		// COMMANDS COMING FROM IO SUBSYSTEM (MID-LAYER)
343962306a36Sopenharmony_ci		scb->sno		= i + MBOX_MAX_SCSI_CMDS;
344062306a36Sopenharmony_ci
344162306a36Sopenharmony_ci		scb->scp		= NULL;
344262306a36Sopenharmony_ci		scb->state		= SCB_FREE;
344362306a36Sopenharmony_ci		scb->dma_direction	= DMA_NONE;
344462306a36Sopenharmony_ci		scb->dma_type		= MRAID_DMA_NONE;
344562306a36Sopenharmony_ci		scb->dev_channel	= -1;
344662306a36Sopenharmony_ci		scb->dev_target		= -1;
344762306a36Sopenharmony_ci
344862306a36Sopenharmony_ci		// put scb in the free pool
344962306a36Sopenharmony_ci		list_add_tail(&scb->list, &adapter->uscb_pool);
345062306a36Sopenharmony_ci	}
345162306a36Sopenharmony_ci
345262306a36Sopenharmony_ci	adp.unique_id		= adapter->unique_id;
345362306a36Sopenharmony_ci	adp.drvr_type		= DRVRTYPE_MBOX;
345462306a36Sopenharmony_ci	adp.drvr_data		= (unsigned long)adapter;
345562306a36Sopenharmony_ci	adp.pdev		= adapter->pdev;
345662306a36Sopenharmony_ci	adp.issue_uioc		= megaraid_mbox_mm_handler;
345762306a36Sopenharmony_ci	adp.timeout		= MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT;
345862306a36Sopenharmony_ci	adp.max_kioc		= MBOX_MAX_USER_CMDS;
345962306a36Sopenharmony_ci
346062306a36Sopenharmony_ci	if ((rval = mraid_mm_register_adp(&adp)) != 0) {
346162306a36Sopenharmony_ci
346262306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
346362306a36Sopenharmony_ci			"megaraid mbox: did not register with CMM\n"));
346462306a36Sopenharmony_ci
346562306a36Sopenharmony_ci		kfree(adapter->uscb_list);
346662306a36Sopenharmony_ci	}
346762306a36Sopenharmony_ci
346862306a36Sopenharmony_ci	return rval;
346962306a36Sopenharmony_ci}
347062306a36Sopenharmony_ci
347162306a36Sopenharmony_ci
347262306a36Sopenharmony_ci/**
347362306a36Sopenharmony_ci * megaraid_cmm_unregister - un-register with the management module
347462306a36Sopenharmony_ci * @adapter		: HBA soft state
347562306a36Sopenharmony_ci *
347662306a36Sopenharmony_ci * Un-register with the management module.
347762306a36Sopenharmony_ci * FIXME: mgmt module must return failure for unregister if it has pending
347862306a36Sopenharmony_ci * commands in LLD.
347962306a36Sopenharmony_ci */
348062306a36Sopenharmony_cistatic int
348162306a36Sopenharmony_cimegaraid_cmm_unregister(adapter_t *adapter)
348262306a36Sopenharmony_ci{
348362306a36Sopenharmony_ci	kfree(adapter->uscb_list);
348462306a36Sopenharmony_ci	mraid_mm_unregister_adp(adapter->unique_id);
348562306a36Sopenharmony_ci	return 0;
348662306a36Sopenharmony_ci}
348762306a36Sopenharmony_ci
348862306a36Sopenharmony_ci
348962306a36Sopenharmony_ci/**
349062306a36Sopenharmony_ci * megaraid_mbox_mm_handler - interface for CMM to issue commands to LLD
349162306a36Sopenharmony_ci * @drvr_data		: LLD specific data
349262306a36Sopenharmony_ci * @kioc		: CMM interface packet
349362306a36Sopenharmony_ci * @action		: command action
349462306a36Sopenharmony_ci *
349562306a36Sopenharmony_ci * This routine is invoked whenever the Common Management Module (CMM) has a
349662306a36Sopenharmony_ci * command for us. The 'action' parameter specifies if this is a new command
349762306a36Sopenharmony_ci * or otherwise.
349862306a36Sopenharmony_ci */
349962306a36Sopenharmony_cistatic int
350062306a36Sopenharmony_cimegaraid_mbox_mm_handler(unsigned long drvr_data, uioc_t *kioc, uint32_t action)
350162306a36Sopenharmony_ci{
350262306a36Sopenharmony_ci	adapter_t *adapter;
350362306a36Sopenharmony_ci
350462306a36Sopenharmony_ci	if (action != IOCTL_ISSUE) {
350562306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
350662306a36Sopenharmony_ci			"megaraid: unsupported management action:%#2x\n",
350762306a36Sopenharmony_ci			action));
350862306a36Sopenharmony_ci		return (-ENOTSUPP);
350962306a36Sopenharmony_ci	}
351062306a36Sopenharmony_ci
351162306a36Sopenharmony_ci	adapter = (adapter_t *)drvr_data;
351262306a36Sopenharmony_ci
351362306a36Sopenharmony_ci	// make sure this adapter is not being detached right now.
351462306a36Sopenharmony_ci	if (atomic_read(&adapter->being_detached)) {
351562306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
351662306a36Sopenharmony_ci			"megaraid: reject management request, detaching\n"));
351762306a36Sopenharmony_ci		return (-ENODEV);
351862306a36Sopenharmony_ci	}
351962306a36Sopenharmony_ci
352062306a36Sopenharmony_ci	switch (kioc->opcode) {
352162306a36Sopenharmony_ci
352262306a36Sopenharmony_ci	case GET_ADAP_INFO:
352362306a36Sopenharmony_ci
352462306a36Sopenharmony_ci		kioc->status =  gather_hbainfo(adapter, (mraid_hba_info_t *)
352562306a36Sopenharmony_ci					(unsigned long)kioc->buf_vaddr);
352662306a36Sopenharmony_ci
352762306a36Sopenharmony_ci		kioc->done(kioc);
352862306a36Sopenharmony_ci
352962306a36Sopenharmony_ci		return kioc->status;
353062306a36Sopenharmony_ci
353162306a36Sopenharmony_ci	case MBOX_CMD:
353262306a36Sopenharmony_ci
353362306a36Sopenharmony_ci		return megaraid_mbox_mm_command(adapter, kioc);
353462306a36Sopenharmony_ci
353562306a36Sopenharmony_ci	default:
353662306a36Sopenharmony_ci		kioc->status = (-EINVAL);
353762306a36Sopenharmony_ci		kioc->done(kioc);
353862306a36Sopenharmony_ci		return (-EINVAL);
353962306a36Sopenharmony_ci	}
354062306a36Sopenharmony_ci
354162306a36Sopenharmony_ci	return 0;	// not reached
354262306a36Sopenharmony_ci}
354362306a36Sopenharmony_ci
354462306a36Sopenharmony_ci/**
354562306a36Sopenharmony_ci * megaraid_mbox_mm_command - issues commands routed through CMM
354662306a36Sopenharmony_ci * @adapter		: HBA soft state
354762306a36Sopenharmony_ci * @kioc		: management command packet
354862306a36Sopenharmony_ci *
354962306a36Sopenharmony_ci * Issues commands, which are routed through the management module.
355062306a36Sopenharmony_ci */
355162306a36Sopenharmony_cistatic int
355262306a36Sopenharmony_cimegaraid_mbox_mm_command(adapter_t *adapter, uioc_t *kioc)
355362306a36Sopenharmony_ci{
355462306a36Sopenharmony_ci	struct list_head	*head = &adapter->uscb_pool;
355562306a36Sopenharmony_ci	mbox64_t		*mbox64;
355662306a36Sopenharmony_ci	uint8_t			*raw_mbox;
355762306a36Sopenharmony_ci	scb_t			*scb;
355862306a36Sopenharmony_ci	mbox_ccb_t		*ccb;
355962306a36Sopenharmony_ci	unsigned long		flags;
356062306a36Sopenharmony_ci
356162306a36Sopenharmony_ci	// detach one scb from free pool
356262306a36Sopenharmony_ci	spin_lock_irqsave(USER_FREE_LIST_LOCK(adapter), flags);
356362306a36Sopenharmony_ci
356462306a36Sopenharmony_ci	if (list_empty(head)) {	// should never happen because of CMM
356562306a36Sopenharmony_ci
356662306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
356762306a36Sopenharmony_ci			"megaraid mbox: bug in cmm handler, lost resources\n"));
356862306a36Sopenharmony_ci
356962306a36Sopenharmony_ci		spin_unlock_irqrestore(USER_FREE_LIST_LOCK(adapter), flags);
357062306a36Sopenharmony_ci
357162306a36Sopenharmony_ci		return (-EINVAL);
357262306a36Sopenharmony_ci	}
357362306a36Sopenharmony_ci
357462306a36Sopenharmony_ci	scb = list_entry(head->next, scb_t, list);
357562306a36Sopenharmony_ci	list_del_init(&scb->list);
357662306a36Sopenharmony_ci
357762306a36Sopenharmony_ci	spin_unlock_irqrestore(USER_FREE_LIST_LOCK(adapter), flags);
357862306a36Sopenharmony_ci
357962306a36Sopenharmony_ci	scb->state		= SCB_ACTIVE;
358062306a36Sopenharmony_ci	scb->dma_type		= MRAID_DMA_NONE;
358162306a36Sopenharmony_ci	scb->dma_direction	= DMA_NONE;
358262306a36Sopenharmony_ci
358362306a36Sopenharmony_ci	ccb		= (mbox_ccb_t *)scb->ccb;
358462306a36Sopenharmony_ci	mbox64		= (mbox64_t *)(unsigned long)kioc->cmdbuf;
358562306a36Sopenharmony_ci	raw_mbox	= (uint8_t *)&mbox64->mbox32;
358662306a36Sopenharmony_ci
358762306a36Sopenharmony_ci	memcpy(ccb->mbox64, mbox64, sizeof(mbox64_t));
358862306a36Sopenharmony_ci
358962306a36Sopenharmony_ci	scb->gp		= (unsigned long)kioc;
359062306a36Sopenharmony_ci
359162306a36Sopenharmony_ci	/*
359262306a36Sopenharmony_ci	 * If it is a logdrv random delete operation, we have to wait till
359362306a36Sopenharmony_ci	 * there are no outstanding cmds at the fw and then issue it directly
359462306a36Sopenharmony_ci	 */
359562306a36Sopenharmony_ci	if (raw_mbox[0] == FC_DEL_LOGDRV && raw_mbox[2] == OP_DEL_LOGDRV) {
359662306a36Sopenharmony_ci
359762306a36Sopenharmony_ci		if (wait_till_fw_empty(adapter)) {
359862306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_NOTICE
359962306a36Sopenharmony_ci				"megaraid mbox: LD delete, timed out\n"));
360062306a36Sopenharmony_ci
360162306a36Sopenharmony_ci			kioc->status = -ETIME;
360262306a36Sopenharmony_ci
360362306a36Sopenharmony_ci			scb->status = -1;
360462306a36Sopenharmony_ci
360562306a36Sopenharmony_ci			megaraid_mbox_mm_done(adapter, scb);
360662306a36Sopenharmony_ci
360762306a36Sopenharmony_ci			return (-ETIME);
360862306a36Sopenharmony_ci		}
360962306a36Sopenharmony_ci
361062306a36Sopenharmony_ci		INIT_LIST_HEAD(&scb->list);
361162306a36Sopenharmony_ci
361262306a36Sopenharmony_ci		scb->state = SCB_ISSUED;
361362306a36Sopenharmony_ci		if (mbox_post_cmd(adapter, scb) != 0) {
361462306a36Sopenharmony_ci
361562306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_NOTICE
361662306a36Sopenharmony_ci				"megaraid mbox: LD delete, mailbox busy\n"));
361762306a36Sopenharmony_ci
361862306a36Sopenharmony_ci			kioc->status = -EBUSY;
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_ci			scb->status = -1;
362162306a36Sopenharmony_ci
362262306a36Sopenharmony_ci			megaraid_mbox_mm_done(adapter, scb);
362362306a36Sopenharmony_ci
362462306a36Sopenharmony_ci			return (-EBUSY);
362562306a36Sopenharmony_ci		}
362662306a36Sopenharmony_ci
362762306a36Sopenharmony_ci		return 0;
362862306a36Sopenharmony_ci	}
362962306a36Sopenharmony_ci
363062306a36Sopenharmony_ci	// put the command on the pending list and execute
363162306a36Sopenharmony_ci	megaraid_mbox_runpendq(adapter, scb);
363262306a36Sopenharmony_ci
363362306a36Sopenharmony_ci	return 0;
363462306a36Sopenharmony_ci}
363562306a36Sopenharmony_ci
363662306a36Sopenharmony_ci
363762306a36Sopenharmony_cistatic int
363862306a36Sopenharmony_ciwait_till_fw_empty(adapter_t *adapter)
363962306a36Sopenharmony_ci{
364062306a36Sopenharmony_ci	unsigned long	flags = 0;
364162306a36Sopenharmony_ci	int		i;
364262306a36Sopenharmony_ci
364362306a36Sopenharmony_ci
364462306a36Sopenharmony_ci	/*
364562306a36Sopenharmony_ci	 * Set the quiescent flag to stop issuing cmds to FW.
364662306a36Sopenharmony_ci	 */
364762306a36Sopenharmony_ci	spin_lock_irqsave(&adapter->lock, flags);
364862306a36Sopenharmony_ci	adapter->quiescent++;
364962306a36Sopenharmony_ci	spin_unlock_irqrestore(&adapter->lock, flags);
365062306a36Sopenharmony_ci
365162306a36Sopenharmony_ci	/*
365262306a36Sopenharmony_ci	 * Wait till there are no more cmds outstanding at FW. Try for at most
365362306a36Sopenharmony_ci	 * 60 seconds
365462306a36Sopenharmony_ci	 */
365562306a36Sopenharmony_ci	for (i = 0; i < 60 && adapter->outstanding_cmds; i++) {
365662306a36Sopenharmony_ci		con_log(CL_DLEVEL1, (KERN_INFO
365762306a36Sopenharmony_ci			"megaraid: FW has %d pending commands\n",
365862306a36Sopenharmony_ci			adapter->outstanding_cmds));
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_ci		msleep(1000);
366162306a36Sopenharmony_ci	}
366262306a36Sopenharmony_ci
366362306a36Sopenharmony_ci	return adapter->outstanding_cmds;
366462306a36Sopenharmony_ci}
366562306a36Sopenharmony_ci
366662306a36Sopenharmony_ci
366762306a36Sopenharmony_ci/**
366862306a36Sopenharmony_ci * megaraid_mbox_mm_done - callback for CMM commands
366962306a36Sopenharmony_ci * @adapter	: HBA soft state
367062306a36Sopenharmony_ci * @scb		: completed command
367162306a36Sopenharmony_ci *
367262306a36Sopenharmony_ci * Callback routine for internal commands originated from the management
367362306a36Sopenharmony_ci * module.
367462306a36Sopenharmony_ci */
367562306a36Sopenharmony_cistatic void
367662306a36Sopenharmony_cimegaraid_mbox_mm_done(adapter_t *adapter, scb_t *scb)
367762306a36Sopenharmony_ci{
367862306a36Sopenharmony_ci	uioc_t			*kioc;
367962306a36Sopenharmony_ci	mbox64_t		*mbox64;
368062306a36Sopenharmony_ci	uint8_t			*raw_mbox;
368162306a36Sopenharmony_ci	unsigned long		flags;
368262306a36Sopenharmony_ci
368362306a36Sopenharmony_ci	kioc			= (uioc_t *)scb->gp;
368462306a36Sopenharmony_ci	mbox64			= (mbox64_t *)(unsigned long)kioc->cmdbuf;
368562306a36Sopenharmony_ci	mbox64->mbox32.status	= scb->status;
368662306a36Sopenharmony_ci	raw_mbox		= (uint8_t *)&mbox64->mbox32;
368762306a36Sopenharmony_ci
368862306a36Sopenharmony_ci
368962306a36Sopenharmony_ci	// put scb in the free pool
369062306a36Sopenharmony_ci	scb->state	= SCB_FREE;
369162306a36Sopenharmony_ci	scb->scp	= NULL;
369262306a36Sopenharmony_ci
369362306a36Sopenharmony_ci	spin_lock_irqsave(USER_FREE_LIST_LOCK(adapter), flags);
369462306a36Sopenharmony_ci
369562306a36Sopenharmony_ci	list_add(&scb->list, &adapter->uscb_pool);
369662306a36Sopenharmony_ci
369762306a36Sopenharmony_ci	spin_unlock_irqrestore(USER_FREE_LIST_LOCK(adapter), flags);
369862306a36Sopenharmony_ci
369962306a36Sopenharmony_ci	// if a delete logical drive operation succeeded, restart the
370062306a36Sopenharmony_ci	// controller
370162306a36Sopenharmony_ci	if (raw_mbox[0] == FC_DEL_LOGDRV && raw_mbox[2] == OP_DEL_LOGDRV) {
370262306a36Sopenharmony_ci
370362306a36Sopenharmony_ci		adapter->quiescent--;
370462306a36Sopenharmony_ci
370562306a36Sopenharmony_ci		megaraid_mbox_runpendq(adapter, NULL);
370662306a36Sopenharmony_ci	}
370762306a36Sopenharmony_ci
370862306a36Sopenharmony_ci	kioc->done(kioc);
370962306a36Sopenharmony_ci
371062306a36Sopenharmony_ci	return;
371162306a36Sopenharmony_ci}
371262306a36Sopenharmony_ci
371362306a36Sopenharmony_ci
371462306a36Sopenharmony_ci/**
371562306a36Sopenharmony_ci * gather_hbainfo - HBA characteristics for the applications
371662306a36Sopenharmony_ci * @adapter		: HBA soft state
371762306a36Sopenharmony_ci * @hinfo		: pointer to the caller's host info strucuture
371862306a36Sopenharmony_ci */
371962306a36Sopenharmony_cistatic int
372062306a36Sopenharmony_cigather_hbainfo(adapter_t *adapter, mraid_hba_info_t *hinfo)
372162306a36Sopenharmony_ci{
372262306a36Sopenharmony_ci	hinfo->pci_vendor_id	= adapter->pdev->vendor;
372362306a36Sopenharmony_ci	hinfo->pci_device_id	= adapter->pdev->device;
372462306a36Sopenharmony_ci	hinfo->subsys_vendor_id	= adapter->pdev->subsystem_vendor;
372562306a36Sopenharmony_ci	hinfo->subsys_device_id	= adapter->pdev->subsystem_device;
372662306a36Sopenharmony_ci
372762306a36Sopenharmony_ci	hinfo->pci_bus		= adapter->pdev->bus->number;
372862306a36Sopenharmony_ci	hinfo->pci_dev_fn	= adapter->pdev->devfn;
372962306a36Sopenharmony_ci	hinfo->pci_slot		= PCI_SLOT(adapter->pdev->devfn);
373062306a36Sopenharmony_ci	hinfo->irq		= adapter->host->irq;
373162306a36Sopenharmony_ci	hinfo->baseport		= ADAP2RAIDDEV(adapter)->baseport;
373262306a36Sopenharmony_ci
373362306a36Sopenharmony_ci	hinfo->unique_id	= (hinfo->pci_bus << 8) | adapter->pdev->devfn;
373462306a36Sopenharmony_ci	hinfo->host_no		= adapter->host->host_no;
373562306a36Sopenharmony_ci
373662306a36Sopenharmony_ci	return 0;
373762306a36Sopenharmony_ci}
373862306a36Sopenharmony_ci
373962306a36Sopenharmony_ci/*
374062306a36Sopenharmony_ci * END: Interface for the common management module
374162306a36Sopenharmony_ci */
374262306a36Sopenharmony_ci
374362306a36Sopenharmony_ci
374462306a36Sopenharmony_ci
374562306a36Sopenharmony_ci/**
374662306a36Sopenharmony_ci * megaraid_sysfs_alloc_resources - allocate sysfs related resources
374762306a36Sopenharmony_ci * @adapter	: controller's soft state
374862306a36Sopenharmony_ci *
374962306a36Sopenharmony_ci * Allocate packets required to issue FW calls whenever the sysfs attributes
375062306a36Sopenharmony_ci * are read. These attributes would require up-to-date information from the
375162306a36Sopenharmony_ci * FW. Also set up resources for mutual exclusion to share these resources and
375262306a36Sopenharmony_ci * the wait queue.
375362306a36Sopenharmony_ci *
375462306a36Sopenharmony_ci * Return 0 on success.
375562306a36Sopenharmony_ci * Return -ERROR_CODE on failure.
375662306a36Sopenharmony_ci */
375762306a36Sopenharmony_cistatic int
375862306a36Sopenharmony_cimegaraid_sysfs_alloc_resources(adapter_t *adapter)
375962306a36Sopenharmony_ci{
376062306a36Sopenharmony_ci	mraid_device_t	*raid_dev = ADAP2RAIDDEV(adapter);
376162306a36Sopenharmony_ci	int		rval = 0;
376262306a36Sopenharmony_ci
376362306a36Sopenharmony_ci	raid_dev->sysfs_uioc = kmalloc(sizeof(uioc_t), GFP_KERNEL);
376462306a36Sopenharmony_ci
376562306a36Sopenharmony_ci	raid_dev->sysfs_mbox64 = kmalloc(sizeof(mbox64_t), GFP_KERNEL);
376662306a36Sopenharmony_ci
376762306a36Sopenharmony_ci	raid_dev->sysfs_buffer = dma_alloc_coherent(&adapter->pdev->dev,
376862306a36Sopenharmony_ci			PAGE_SIZE, &raid_dev->sysfs_buffer_dma, GFP_KERNEL);
376962306a36Sopenharmony_ci
377062306a36Sopenharmony_ci	if (!raid_dev->sysfs_uioc || !raid_dev->sysfs_mbox64 ||
377162306a36Sopenharmony_ci		!raid_dev->sysfs_buffer) {
377262306a36Sopenharmony_ci
377362306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_WARNING
377462306a36Sopenharmony_ci			"megaraid: out of memory, %s %d\n", __func__,
377562306a36Sopenharmony_ci			__LINE__));
377662306a36Sopenharmony_ci
377762306a36Sopenharmony_ci		rval = -ENOMEM;
377862306a36Sopenharmony_ci
377962306a36Sopenharmony_ci		megaraid_sysfs_free_resources(adapter);
378062306a36Sopenharmony_ci	}
378162306a36Sopenharmony_ci
378262306a36Sopenharmony_ci	mutex_init(&raid_dev->sysfs_mtx);
378362306a36Sopenharmony_ci
378462306a36Sopenharmony_ci	init_waitqueue_head(&raid_dev->sysfs_wait_q);
378562306a36Sopenharmony_ci
378662306a36Sopenharmony_ci	return rval;
378762306a36Sopenharmony_ci}
378862306a36Sopenharmony_ci
378962306a36Sopenharmony_ci
379062306a36Sopenharmony_ci/**
379162306a36Sopenharmony_ci * megaraid_sysfs_free_resources - free sysfs related resources
379262306a36Sopenharmony_ci * @adapter	: controller's soft state
379362306a36Sopenharmony_ci *
379462306a36Sopenharmony_ci * Free packets allocated for sysfs FW commands
379562306a36Sopenharmony_ci */
379662306a36Sopenharmony_cistatic void
379762306a36Sopenharmony_cimegaraid_sysfs_free_resources(adapter_t *adapter)
379862306a36Sopenharmony_ci{
379962306a36Sopenharmony_ci	mraid_device_t	*raid_dev = ADAP2RAIDDEV(adapter);
380062306a36Sopenharmony_ci
380162306a36Sopenharmony_ci	kfree(raid_dev->sysfs_uioc);
380262306a36Sopenharmony_ci	kfree(raid_dev->sysfs_mbox64);
380362306a36Sopenharmony_ci
380462306a36Sopenharmony_ci	if (raid_dev->sysfs_buffer) {
380562306a36Sopenharmony_ci		dma_free_coherent(&adapter->pdev->dev, PAGE_SIZE,
380662306a36Sopenharmony_ci			raid_dev->sysfs_buffer, raid_dev->sysfs_buffer_dma);
380762306a36Sopenharmony_ci	}
380862306a36Sopenharmony_ci}
380962306a36Sopenharmony_ci
381062306a36Sopenharmony_ci
381162306a36Sopenharmony_ci/**
381262306a36Sopenharmony_ci * megaraid_sysfs_get_ldmap_done - callback for get ldmap
381362306a36Sopenharmony_ci * @uioc	: completed packet
381462306a36Sopenharmony_ci *
381562306a36Sopenharmony_ci * Callback routine called in the ISR/tasklet context for get ldmap call
381662306a36Sopenharmony_ci */
381762306a36Sopenharmony_cistatic void
381862306a36Sopenharmony_cimegaraid_sysfs_get_ldmap_done(uioc_t *uioc)
381962306a36Sopenharmony_ci{
382062306a36Sopenharmony_ci	adapter_t	*adapter = (adapter_t *)uioc->buf_vaddr;
382162306a36Sopenharmony_ci	mraid_device_t	*raid_dev = ADAP2RAIDDEV(adapter);
382262306a36Sopenharmony_ci
382362306a36Sopenharmony_ci	uioc->status = 0;
382462306a36Sopenharmony_ci
382562306a36Sopenharmony_ci	wake_up(&raid_dev->sysfs_wait_q);
382662306a36Sopenharmony_ci}
382762306a36Sopenharmony_ci
382862306a36Sopenharmony_ci/**
382962306a36Sopenharmony_ci * megaraid_sysfs_get_ldmap_timeout - timeout handling for get ldmap
383062306a36Sopenharmony_ci * @t	: timed out timer
383162306a36Sopenharmony_ci *
383262306a36Sopenharmony_ci * Timeout routine to recover and return to application, in case the adapter
383362306a36Sopenharmony_ci * has stopped responding. A timeout of 60 seconds for this command seems like
383462306a36Sopenharmony_ci * a good value.
383562306a36Sopenharmony_ci */
383662306a36Sopenharmony_cistatic void
383762306a36Sopenharmony_cimegaraid_sysfs_get_ldmap_timeout(struct timer_list *t)
383862306a36Sopenharmony_ci{
383962306a36Sopenharmony_ci	struct uioc_timeout *timeout = from_timer(timeout, t, timer);
384062306a36Sopenharmony_ci	uioc_t		*uioc = timeout->uioc;
384162306a36Sopenharmony_ci	adapter_t	*adapter = (adapter_t *)uioc->buf_vaddr;
384262306a36Sopenharmony_ci	mraid_device_t	*raid_dev = ADAP2RAIDDEV(adapter);
384362306a36Sopenharmony_ci
384462306a36Sopenharmony_ci	uioc->status = -ETIME;
384562306a36Sopenharmony_ci
384662306a36Sopenharmony_ci	wake_up(&raid_dev->sysfs_wait_q);
384762306a36Sopenharmony_ci}
384862306a36Sopenharmony_ci
384962306a36Sopenharmony_ci
385062306a36Sopenharmony_ci/**
385162306a36Sopenharmony_ci * megaraid_sysfs_get_ldmap - get update logical drive map
385262306a36Sopenharmony_ci * @adapter	: controller's soft state
385362306a36Sopenharmony_ci *
385462306a36Sopenharmony_ci * This routine will be called whenever user reads the logical drive
385562306a36Sopenharmony_ci * attributes, go get the current logical drive mapping table from the
385662306a36Sopenharmony_ci * firmware. We use the management API's to issue commands to the controller.
385762306a36Sopenharmony_ci *
385862306a36Sopenharmony_ci * NOTE: The commands issuance functionality is not generalized and
385962306a36Sopenharmony_ci * implemented in context of "get ld map" command only. If required, the
386062306a36Sopenharmony_ci * command issuance logical can be trivially pulled out and implemented as a
386162306a36Sopenharmony_ci * standalone library. For now, this should suffice since there is no other
386262306a36Sopenharmony_ci * user of this interface.
386362306a36Sopenharmony_ci *
386462306a36Sopenharmony_ci * Return 0 on success.
386562306a36Sopenharmony_ci * Return -1 on failure.
386662306a36Sopenharmony_ci */
386762306a36Sopenharmony_cistatic int
386862306a36Sopenharmony_cimegaraid_sysfs_get_ldmap(adapter_t *adapter)
386962306a36Sopenharmony_ci{
387062306a36Sopenharmony_ci	mraid_device_t		*raid_dev = ADAP2RAIDDEV(adapter);
387162306a36Sopenharmony_ci	uioc_t			*uioc;
387262306a36Sopenharmony_ci	mbox64_t		*mbox64;
387362306a36Sopenharmony_ci	mbox_t			*mbox;
387462306a36Sopenharmony_ci	char			*raw_mbox;
387562306a36Sopenharmony_ci	struct uioc_timeout	timeout;
387662306a36Sopenharmony_ci	caddr_t			ldmap;
387762306a36Sopenharmony_ci	int			rval = 0;
387862306a36Sopenharmony_ci
387962306a36Sopenharmony_ci	/*
388062306a36Sopenharmony_ci	 * Allow only one read at a time to go through the sysfs attributes
388162306a36Sopenharmony_ci	 */
388262306a36Sopenharmony_ci	mutex_lock(&raid_dev->sysfs_mtx);
388362306a36Sopenharmony_ci
388462306a36Sopenharmony_ci	uioc	= raid_dev->sysfs_uioc;
388562306a36Sopenharmony_ci	mbox64	= raid_dev->sysfs_mbox64;
388662306a36Sopenharmony_ci	ldmap	= raid_dev->sysfs_buffer;
388762306a36Sopenharmony_ci
388862306a36Sopenharmony_ci	memset(uioc, 0, sizeof(uioc_t));
388962306a36Sopenharmony_ci	memset(mbox64, 0, sizeof(mbox64_t));
389062306a36Sopenharmony_ci	memset(ldmap, 0, sizeof(raid_dev->curr_ldmap));
389162306a36Sopenharmony_ci
389262306a36Sopenharmony_ci	mbox		= &mbox64->mbox32;
389362306a36Sopenharmony_ci	raw_mbox	= (char *)mbox;
389462306a36Sopenharmony_ci	uioc->cmdbuf    = (uint64_t)(unsigned long)mbox64;
389562306a36Sopenharmony_ci	uioc->buf_vaddr	= (caddr_t)adapter;
389662306a36Sopenharmony_ci	uioc->status	= -ENODATA;
389762306a36Sopenharmony_ci	uioc->done	= megaraid_sysfs_get_ldmap_done;
389862306a36Sopenharmony_ci
389962306a36Sopenharmony_ci	/*
390062306a36Sopenharmony_ci	 * Prepare the mailbox packet to get the current logical drive mapping
390162306a36Sopenharmony_ci	 * table
390262306a36Sopenharmony_ci	 */
390362306a36Sopenharmony_ci	mbox->xferaddr = (uint32_t)raid_dev->sysfs_buffer_dma;
390462306a36Sopenharmony_ci
390562306a36Sopenharmony_ci	raw_mbox[0] = FC_DEL_LOGDRV;
390662306a36Sopenharmony_ci	raw_mbox[2] = OP_GET_LDID_MAP;
390762306a36Sopenharmony_ci
390862306a36Sopenharmony_ci	/*
390962306a36Sopenharmony_ci	 * Setup a timer to recover from a non-responding controller
391062306a36Sopenharmony_ci	 */
391162306a36Sopenharmony_ci	timeout.uioc = uioc;
391262306a36Sopenharmony_ci	timer_setup_on_stack(&timeout.timer,
391362306a36Sopenharmony_ci			     megaraid_sysfs_get_ldmap_timeout, 0);
391462306a36Sopenharmony_ci
391562306a36Sopenharmony_ci	timeout.timer.expires		= jiffies + 60 * HZ;
391662306a36Sopenharmony_ci	add_timer(&timeout.timer);
391762306a36Sopenharmony_ci
391862306a36Sopenharmony_ci	/*
391962306a36Sopenharmony_ci	 * Send the command to the firmware
392062306a36Sopenharmony_ci	 */
392162306a36Sopenharmony_ci	rval = megaraid_mbox_mm_command(adapter, uioc);
392262306a36Sopenharmony_ci
392362306a36Sopenharmony_ci	if (rval == 0) {	// command successfully issued
392462306a36Sopenharmony_ci		wait_event(raid_dev->sysfs_wait_q, (uioc->status != -ENODATA));
392562306a36Sopenharmony_ci
392662306a36Sopenharmony_ci		/*
392762306a36Sopenharmony_ci		 * Check if the command timed out
392862306a36Sopenharmony_ci		 */
392962306a36Sopenharmony_ci		if (uioc->status == -ETIME) {
393062306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_NOTICE
393162306a36Sopenharmony_ci				"megaraid: sysfs get ld map timed out\n"));
393262306a36Sopenharmony_ci
393362306a36Sopenharmony_ci			rval = -ETIME;
393462306a36Sopenharmony_ci		}
393562306a36Sopenharmony_ci		else {
393662306a36Sopenharmony_ci			rval = mbox->status;
393762306a36Sopenharmony_ci		}
393862306a36Sopenharmony_ci
393962306a36Sopenharmony_ci		if (rval == 0) {
394062306a36Sopenharmony_ci			memcpy(raid_dev->curr_ldmap, ldmap,
394162306a36Sopenharmony_ci				sizeof(raid_dev->curr_ldmap));
394262306a36Sopenharmony_ci		}
394362306a36Sopenharmony_ci		else {
394462306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_NOTICE
394562306a36Sopenharmony_ci				"megaraid: get ld map failed with %x\n", rval));
394662306a36Sopenharmony_ci		}
394762306a36Sopenharmony_ci	}
394862306a36Sopenharmony_ci	else {
394962306a36Sopenharmony_ci		con_log(CL_ANN, (KERN_NOTICE
395062306a36Sopenharmony_ci			"megaraid: could not issue ldmap command:%x\n", rval));
395162306a36Sopenharmony_ci	}
395262306a36Sopenharmony_ci
395362306a36Sopenharmony_ci
395462306a36Sopenharmony_ci	del_timer_sync(&timeout.timer);
395562306a36Sopenharmony_ci	destroy_timer_on_stack(&timeout.timer);
395662306a36Sopenharmony_ci
395762306a36Sopenharmony_ci	mutex_unlock(&raid_dev->sysfs_mtx);
395862306a36Sopenharmony_ci
395962306a36Sopenharmony_ci	return rval;
396062306a36Sopenharmony_ci}
396162306a36Sopenharmony_ci
396262306a36Sopenharmony_ci
396362306a36Sopenharmony_ci/**
396462306a36Sopenharmony_ci * megaraid_mbox_app_hndl_show - display application handle for this adapter
396562306a36Sopenharmony_ci * @dev		: class device object representation for the host
396662306a36Sopenharmony_ci * @attr	: device attribute (unused)
396762306a36Sopenharmony_ci * @buf		: buffer to send data to
396862306a36Sopenharmony_ci *
396962306a36Sopenharmony_ci * Display the handle used by the applications while executing management
397062306a36Sopenharmony_ci * tasks on the adapter. We invoke a management module API to get the adapter
397162306a36Sopenharmony_ci * handle, since we do not interface with applications directly.
397262306a36Sopenharmony_ci */
397362306a36Sopenharmony_cistatic ssize_t
397462306a36Sopenharmony_cimegaraid_mbox_app_hndl_show(struct device *dev, struct device_attribute *attr, char *buf)
397562306a36Sopenharmony_ci{
397662306a36Sopenharmony_ci	struct Scsi_Host *shost = class_to_shost(dev);
397762306a36Sopenharmony_ci	adapter_t	*adapter = (adapter_t *)SCSIHOST2ADAP(shost);
397862306a36Sopenharmony_ci	uint32_t	app_hndl;
397962306a36Sopenharmony_ci
398062306a36Sopenharmony_ci	app_hndl = mraid_mm_adapter_app_handle(adapter->unique_id);
398162306a36Sopenharmony_ci
398262306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n", app_hndl);
398362306a36Sopenharmony_ci}
398462306a36Sopenharmony_ci
398562306a36Sopenharmony_ci
398662306a36Sopenharmony_ci/**
398762306a36Sopenharmony_ci * megaraid_mbox_ld_show - display the logical drive number for this device
398862306a36Sopenharmony_ci * @dev		: device object representation for the scsi device
398962306a36Sopenharmony_ci * @attr	: device attribute to show
399062306a36Sopenharmony_ci * @buf		: buffer to send data to
399162306a36Sopenharmony_ci *
399262306a36Sopenharmony_ci * Display the logical drive number for the device in question, if it a valid
399362306a36Sopenharmony_ci * logical drive. For physical devices, "-1" is returned.
399462306a36Sopenharmony_ci *
399562306a36Sopenharmony_ci * The logical drive number is displayed in following format:
399662306a36Sopenharmony_ci *
399762306a36Sopenharmony_ci * <SCSI ID> <LD NUM> <LD STICKY ID> <APP ADAPTER HANDLE>
399862306a36Sopenharmony_ci *
399962306a36Sopenharmony_ci *   <int>     <int>       <int>            <int>
400062306a36Sopenharmony_ci */
400162306a36Sopenharmony_cistatic ssize_t
400262306a36Sopenharmony_cimegaraid_mbox_ld_show(struct device *dev, struct device_attribute *attr, char *buf)
400362306a36Sopenharmony_ci{
400462306a36Sopenharmony_ci	struct scsi_device *sdev = to_scsi_device(dev);
400562306a36Sopenharmony_ci	adapter_t	*adapter = (adapter_t *)SCSIHOST2ADAP(sdev->host);
400662306a36Sopenharmony_ci	mraid_device_t	*raid_dev = ADAP2RAIDDEV(adapter);
400762306a36Sopenharmony_ci	int		scsi_id = -1;
400862306a36Sopenharmony_ci	int		logical_drv = -1;
400962306a36Sopenharmony_ci	int		ldid_map = -1;
401062306a36Sopenharmony_ci	uint32_t	app_hndl = 0;
401162306a36Sopenharmony_ci	int		mapped_sdev_id;
401262306a36Sopenharmony_ci	int		rval;
401362306a36Sopenharmony_ci	int		i;
401462306a36Sopenharmony_ci
401562306a36Sopenharmony_ci	if (raid_dev->random_del_supported &&
401662306a36Sopenharmony_ci			MRAID_IS_LOGICAL_SDEV(adapter, sdev)) {
401762306a36Sopenharmony_ci
401862306a36Sopenharmony_ci		rval = megaraid_sysfs_get_ldmap(adapter);
401962306a36Sopenharmony_ci		if (rval == 0) {
402062306a36Sopenharmony_ci
402162306a36Sopenharmony_ci			for (i = 0; i < MAX_LOGICAL_DRIVES_40LD; i++) {
402262306a36Sopenharmony_ci
402362306a36Sopenharmony_ci				mapped_sdev_id = sdev->id;
402462306a36Sopenharmony_ci
402562306a36Sopenharmony_ci				if (sdev->id > adapter->init_id) {
402662306a36Sopenharmony_ci					mapped_sdev_id -= 1;
402762306a36Sopenharmony_ci				}
402862306a36Sopenharmony_ci
402962306a36Sopenharmony_ci				if (raid_dev->curr_ldmap[i] == mapped_sdev_id) {
403062306a36Sopenharmony_ci
403162306a36Sopenharmony_ci					scsi_id = sdev->id;
403262306a36Sopenharmony_ci
403362306a36Sopenharmony_ci					logical_drv = i;
403462306a36Sopenharmony_ci
403562306a36Sopenharmony_ci					ldid_map = raid_dev->curr_ldmap[i];
403662306a36Sopenharmony_ci
403762306a36Sopenharmony_ci					app_hndl = mraid_mm_adapter_app_handle(
403862306a36Sopenharmony_ci							adapter->unique_id);
403962306a36Sopenharmony_ci
404062306a36Sopenharmony_ci					break;
404162306a36Sopenharmony_ci				}
404262306a36Sopenharmony_ci			}
404362306a36Sopenharmony_ci		}
404462306a36Sopenharmony_ci		else {
404562306a36Sopenharmony_ci			con_log(CL_ANN, (KERN_NOTICE
404662306a36Sopenharmony_ci				"megaraid: sysfs get ld map failed: %x\n",
404762306a36Sopenharmony_ci				rval));
404862306a36Sopenharmony_ci		}
404962306a36Sopenharmony_ci	}
405062306a36Sopenharmony_ci
405162306a36Sopenharmony_ci	return sysfs_emit(buf, "%d %d %d %d\n", scsi_id, logical_drv,
405262306a36Sopenharmony_ci			ldid_map, app_hndl);
405362306a36Sopenharmony_ci}
405462306a36Sopenharmony_ci
405562306a36Sopenharmony_ci
405662306a36Sopenharmony_ci/*
405762306a36Sopenharmony_ci * END: Mailbox Low Level Driver
405862306a36Sopenharmony_ci */
405962306a36Sopenharmony_cimodule_init(megaraid_init);
406062306a36Sopenharmony_cimodule_exit(megaraid_exit);
4061