18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Marvell 88SE64xx/88SE94xx pci init
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2007 Red Hat, Inc.
68c2ecf20Sopenharmony_ci * Copyright 2008 Marvell. <kewei@marvell.com>
78c2ecf20Sopenharmony_ci * Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
88c2ecf20Sopenharmony_ci*/
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "mv_sas.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ciint interrupt_coalescing = 0x80;
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic struct scsi_transport_template *mvs_stt;
168c2ecf20Sopenharmony_cistatic const struct mvs_chip_info mvs_chips[] = {
178c2ecf20Sopenharmony_ci	[chip_6320] =	{ 1, 2, 0x400, 17, 16, 6,  9, &mvs_64xx_dispatch, },
188c2ecf20Sopenharmony_ci	[chip_6440] =	{ 1, 4, 0x400, 17, 16, 6,  9, &mvs_64xx_dispatch, },
198c2ecf20Sopenharmony_ci	[chip_6485] =	{ 1, 8, 0x800, 33, 32, 6, 10, &mvs_64xx_dispatch, },
208c2ecf20Sopenharmony_ci	[chip_9180] =	{ 2, 4, 0x800, 17, 64, 8,  9, &mvs_94xx_dispatch, },
218c2ecf20Sopenharmony_ci	[chip_9480] =	{ 2, 4, 0x800, 17, 64, 8,  9, &mvs_94xx_dispatch, },
228c2ecf20Sopenharmony_ci	[chip_9445] =	{ 1, 4, 0x800, 17, 64, 8, 11, &mvs_94xx_dispatch, },
238c2ecf20Sopenharmony_ci	[chip_9485] =	{ 2, 4, 0x800, 17, 64, 8, 11, &mvs_94xx_dispatch, },
248c2ecf20Sopenharmony_ci	[chip_1300] =	{ 1, 4, 0x400, 17, 16, 6,  9, &mvs_64xx_dispatch, },
258c2ecf20Sopenharmony_ci	[chip_1320] =	{ 2, 4, 0x800, 17, 64, 8,  9, &mvs_94xx_dispatch, },
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic struct device_attribute *mvst_host_attrs[];
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define SOC_SAS_NUM 2
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic struct scsi_host_template mvs_sht = {
338c2ecf20Sopenharmony_ci	.module			= THIS_MODULE,
348c2ecf20Sopenharmony_ci	.name			= DRV_NAME,
358c2ecf20Sopenharmony_ci	.queuecommand		= sas_queuecommand,
368c2ecf20Sopenharmony_ci	.dma_need_drain		= ata_scsi_dma_need_drain,
378c2ecf20Sopenharmony_ci	.target_alloc		= sas_target_alloc,
388c2ecf20Sopenharmony_ci	.slave_configure	= sas_slave_configure,
398c2ecf20Sopenharmony_ci	.scan_finished		= mvs_scan_finished,
408c2ecf20Sopenharmony_ci	.scan_start		= mvs_scan_start,
418c2ecf20Sopenharmony_ci	.change_queue_depth	= sas_change_queue_depth,
428c2ecf20Sopenharmony_ci	.bios_param		= sas_bios_param,
438c2ecf20Sopenharmony_ci	.can_queue		= 1,
448c2ecf20Sopenharmony_ci	.this_id		= -1,
458c2ecf20Sopenharmony_ci	.sg_tablesize		= SG_ALL,
468c2ecf20Sopenharmony_ci	.max_sectors		= SCSI_DEFAULT_MAX_SECTORS,
478c2ecf20Sopenharmony_ci	.eh_device_reset_handler = sas_eh_device_reset_handler,
488c2ecf20Sopenharmony_ci	.eh_target_reset_handler = sas_eh_target_reset_handler,
498c2ecf20Sopenharmony_ci	.slave_alloc		= sas_slave_alloc,
508c2ecf20Sopenharmony_ci	.target_destroy		= sas_target_destroy,
518c2ecf20Sopenharmony_ci	.ioctl			= sas_ioctl,
528c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
538c2ecf20Sopenharmony_ci	.compat_ioctl		= sas_ioctl,
548c2ecf20Sopenharmony_ci#endif
558c2ecf20Sopenharmony_ci	.shost_attrs		= mvst_host_attrs,
568c2ecf20Sopenharmony_ci	.track_queue_depth	= 1,
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic struct sas_domain_function_template mvs_transport_ops = {
608c2ecf20Sopenharmony_ci	.lldd_dev_found 	= mvs_dev_found,
618c2ecf20Sopenharmony_ci	.lldd_dev_gone		= mvs_dev_gone,
628c2ecf20Sopenharmony_ci	.lldd_execute_task	= mvs_queue_command,
638c2ecf20Sopenharmony_ci	.lldd_control_phy	= mvs_phy_control,
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	.lldd_abort_task	= mvs_abort_task,
668c2ecf20Sopenharmony_ci	.lldd_abort_task_set    = mvs_abort_task_set,
678c2ecf20Sopenharmony_ci	.lldd_clear_aca         = mvs_clear_aca,
688c2ecf20Sopenharmony_ci	.lldd_clear_task_set    = mvs_clear_task_set,
698c2ecf20Sopenharmony_ci	.lldd_I_T_nexus_reset	= mvs_I_T_nexus_reset,
708c2ecf20Sopenharmony_ci	.lldd_lu_reset 		= mvs_lu_reset,
718c2ecf20Sopenharmony_ci	.lldd_query_task	= mvs_query_task,
728c2ecf20Sopenharmony_ci	.lldd_port_formed	= mvs_port_formed,
738c2ecf20Sopenharmony_ci	.lldd_port_deformed     = mvs_port_deformed,
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	.lldd_write_gpio	= mvs_gpio_write,
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void mvs_phy_init(struct mvs_info *mvi, int phy_id)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	struct mvs_phy *phy = &mvi->phy[phy_id];
828c2ecf20Sopenharmony_ci	struct asd_sas_phy *sas_phy = &phy->sas_phy;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	phy->mvi = mvi;
858c2ecf20Sopenharmony_ci	phy->port = NULL;
868c2ecf20Sopenharmony_ci	timer_setup(&phy->timer, NULL, 0);
878c2ecf20Sopenharmony_ci	sas_phy->enabled = (phy_id < mvi->chip->n_phy) ? 1 : 0;
888c2ecf20Sopenharmony_ci	sas_phy->class = SAS;
898c2ecf20Sopenharmony_ci	sas_phy->iproto = SAS_PROTOCOL_ALL;
908c2ecf20Sopenharmony_ci	sas_phy->tproto = 0;
918c2ecf20Sopenharmony_ci	sas_phy->type = PHY_TYPE_PHYSICAL;
928c2ecf20Sopenharmony_ci	sas_phy->role = PHY_ROLE_INITIATOR;
938c2ecf20Sopenharmony_ci	sas_phy->oob_mode = OOB_NOT_CONNECTED;
948c2ecf20Sopenharmony_ci	sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	sas_phy->id = phy_id;
978c2ecf20Sopenharmony_ci	sas_phy->sas_addr = &mvi->sas_addr[0];
988c2ecf20Sopenharmony_ci	sas_phy->frame_rcvd = &phy->frame_rcvd[0];
998c2ecf20Sopenharmony_ci	sas_phy->ha = (struct sas_ha_struct *)mvi->shost->hostdata;
1008c2ecf20Sopenharmony_ci	sas_phy->lldd_phy = phy;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void mvs_free(struct mvs_info *mvi)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct mvs_wq *mwq;
1068c2ecf20Sopenharmony_ci	int slot_nr;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (!mvi)
1098c2ecf20Sopenharmony_ci		return;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (mvi->flags & MVF_FLAG_SOC)
1128c2ecf20Sopenharmony_ci		slot_nr = MVS_SOC_SLOTS;
1138c2ecf20Sopenharmony_ci	else
1148c2ecf20Sopenharmony_ci		slot_nr = MVS_CHIP_SLOT_SZ;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	dma_pool_destroy(mvi->dma_pool);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (mvi->tx)
1198c2ecf20Sopenharmony_ci		dma_free_coherent(mvi->dev,
1208c2ecf20Sopenharmony_ci				  sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ,
1218c2ecf20Sopenharmony_ci				  mvi->tx, mvi->tx_dma);
1228c2ecf20Sopenharmony_ci	if (mvi->rx_fis)
1238c2ecf20Sopenharmony_ci		dma_free_coherent(mvi->dev, MVS_RX_FISL_SZ,
1248c2ecf20Sopenharmony_ci				  mvi->rx_fis, mvi->rx_fis_dma);
1258c2ecf20Sopenharmony_ci	if (mvi->rx)
1268c2ecf20Sopenharmony_ci		dma_free_coherent(mvi->dev,
1278c2ecf20Sopenharmony_ci				  sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1),
1288c2ecf20Sopenharmony_ci				  mvi->rx, mvi->rx_dma);
1298c2ecf20Sopenharmony_ci	if (mvi->slot)
1308c2ecf20Sopenharmony_ci		dma_free_coherent(mvi->dev,
1318c2ecf20Sopenharmony_ci				  sizeof(*mvi->slot) * slot_nr,
1328c2ecf20Sopenharmony_ci				  mvi->slot, mvi->slot_dma);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (mvi->bulk_buffer)
1358c2ecf20Sopenharmony_ci		dma_free_coherent(mvi->dev, TRASH_BUCKET_SIZE,
1368c2ecf20Sopenharmony_ci				  mvi->bulk_buffer, mvi->bulk_buffer_dma);
1378c2ecf20Sopenharmony_ci	if (mvi->bulk_buffer1)
1388c2ecf20Sopenharmony_ci		dma_free_coherent(mvi->dev, TRASH_BUCKET_SIZE,
1398c2ecf20Sopenharmony_ci				  mvi->bulk_buffer1, mvi->bulk_buffer_dma1);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	MVS_CHIP_DISP->chip_iounmap(mvi);
1428c2ecf20Sopenharmony_ci	if (mvi->shost)
1438c2ecf20Sopenharmony_ci		scsi_host_put(mvi->shost);
1448c2ecf20Sopenharmony_ci	list_for_each_entry(mwq, &mvi->wq_list, entry)
1458c2ecf20Sopenharmony_ci		cancel_delayed_work(&mwq->work_q);
1468c2ecf20Sopenharmony_ci	kfree(mvi->tags);
1478c2ecf20Sopenharmony_ci	kfree(mvi);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_MVSAS_TASKLET
1518c2ecf20Sopenharmony_cistatic void mvs_tasklet(unsigned long opaque)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	u32 stat;
1548c2ecf20Sopenharmony_ci	u16 core_nr, i = 0;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	struct mvs_info *mvi;
1578c2ecf20Sopenharmony_ci	struct sas_ha_struct *sha = (struct sas_ha_struct *)opaque;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
1608c2ecf20Sopenharmony_ci	mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (unlikely(!mvi))
1638c2ecf20Sopenharmony_ci		BUG_ON(1);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	stat = MVS_CHIP_DISP->isr_status(mvi, mvi->pdev->irq);
1668c2ecf20Sopenharmony_ci	if (!stat)
1678c2ecf20Sopenharmony_ci		goto out;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	for (i = 0; i < core_nr; i++) {
1708c2ecf20Sopenharmony_ci		mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
1718c2ecf20Sopenharmony_ci		MVS_CHIP_DISP->isr(mvi, mvi->pdev->irq, stat);
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ciout:
1748c2ecf20Sopenharmony_ci	MVS_CHIP_DISP->interrupt_enable(mvi);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci#endif
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic irqreturn_t mvs_interrupt(int irq, void *opaque)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	u32 stat;
1828c2ecf20Sopenharmony_ci	struct mvs_info *mvi;
1838c2ecf20Sopenharmony_ci	struct sas_ha_struct *sha = opaque;
1848c2ecf20Sopenharmony_ci#ifndef CONFIG_SCSI_MVSAS_TASKLET
1858c2ecf20Sopenharmony_ci	u32 i;
1868c2ecf20Sopenharmony_ci	u32 core_nr;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
1898c2ecf20Sopenharmony_ci#endif
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (unlikely(!mvi))
1948c2ecf20Sopenharmony_ci		return IRQ_NONE;
1958c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_MVSAS_TASKLET
1968c2ecf20Sopenharmony_ci	MVS_CHIP_DISP->interrupt_disable(mvi);
1978c2ecf20Sopenharmony_ci#endif
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	stat = MVS_CHIP_DISP->isr_status(mvi, irq);
2008c2ecf20Sopenharmony_ci	if (!stat) {
2018c2ecf20Sopenharmony_ci	#ifdef CONFIG_SCSI_MVSAS_TASKLET
2028c2ecf20Sopenharmony_ci		MVS_CHIP_DISP->interrupt_enable(mvi);
2038c2ecf20Sopenharmony_ci	#endif
2048c2ecf20Sopenharmony_ci		return IRQ_NONE;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_MVSAS_TASKLET
2088c2ecf20Sopenharmony_ci	tasklet_schedule(&((struct mvs_prv_info *)sha->lldd_ha)->mv_tasklet);
2098c2ecf20Sopenharmony_ci#else
2108c2ecf20Sopenharmony_ci	for (i = 0; i < core_nr; i++) {
2118c2ecf20Sopenharmony_ci		mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
2128c2ecf20Sopenharmony_ci		MVS_CHIP_DISP->isr(mvi, irq, stat);
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci#endif
2158c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic int mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	int i = 0, slot_nr;
2218c2ecf20Sopenharmony_ci	char pool_name[32];
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (mvi->flags & MVF_FLAG_SOC)
2248c2ecf20Sopenharmony_ci		slot_nr = MVS_SOC_SLOTS;
2258c2ecf20Sopenharmony_ci	else
2268c2ecf20Sopenharmony_ci		slot_nr = MVS_CHIP_SLOT_SZ;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	spin_lock_init(&mvi->lock);
2298c2ecf20Sopenharmony_ci	for (i = 0; i < mvi->chip->n_phy; i++) {
2308c2ecf20Sopenharmony_ci		mvs_phy_init(mvi, i);
2318c2ecf20Sopenharmony_ci		mvi->port[i].wide_port_phymap = 0;
2328c2ecf20Sopenharmony_ci		mvi->port[i].port_attached = 0;
2338c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&mvi->port[i].list);
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci	for (i = 0; i < MVS_MAX_DEVICES; i++) {
2368c2ecf20Sopenharmony_ci		mvi->devices[i].taskfileset = MVS_ID_NOT_MAPPED;
2378c2ecf20Sopenharmony_ci		mvi->devices[i].dev_type = SAS_PHY_UNUSED;
2388c2ecf20Sopenharmony_ci		mvi->devices[i].device_id = i;
2398c2ecf20Sopenharmony_ci		mvi->devices[i].dev_status = MVS_DEV_NORMAL;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/*
2438c2ecf20Sopenharmony_ci	 * alloc and init our DMA areas
2448c2ecf20Sopenharmony_ci	 */
2458c2ecf20Sopenharmony_ci	mvi->tx = dma_alloc_coherent(mvi->dev,
2468c2ecf20Sopenharmony_ci				     sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ,
2478c2ecf20Sopenharmony_ci				     &mvi->tx_dma, GFP_KERNEL);
2488c2ecf20Sopenharmony_ci	if (!mvi->tx)
2498c2ecf20Sopenharmony_ci		goto err_out;
2508c2ecf20Sopenharmony_ci	mvi->rx_fis = dma_alloc_coherent(mvi->dev, MVS_RX_FISL_SZ,
2518c2ecf20Sopenharmony_ci					 &mvi->rx_fis_dma, GFP_KERNEL);
2528c2ecf20Sopenharmony_ci	if (!mvi->rx_fis)
2538c2ecf20Sopenharmony_ci		goto err_out;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	mvi->rx = dma_alloc_coherent(mvi->dev,
2568c2ecf20Sopenharmony_ci				     sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1),
2578c2ecf20Sopenharmony_ci				     &mvi->rx_dma, GFP_KERNEL);
2588c2ecf20Sopenharmony_ci	if (!mvi->rx)
2598c2ecf20Sopenharmony_ci		goto err_out;
2608c2ecf20Sopenharmony_ci	mvi->rx[0] = cpu_to_le32(0xfff);
2618c2ecf20Sopenharmony_ci	mvi->rx_cons = 0xfff;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	mvi->slot = dma_alloc_coherent(mvi->dev,
2648c2ecf20Sopenharmony_ci				       sizeof(*mvi->slot) * slot_nr,
2658c2ecf20Sopenharmony_ci				       &mvi->slot_dma, GFP_KERNEL);
2668c2ecf20Sopenharmony_ci	if (!mvi->slot)
2678c2ecf20Sopenharmony_ci		goto err_out;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	mvi->bulk_buffer = dma_alloc_coherent(mvi->dev,
2708c2ecf20Sopenharmony_ci				       TRASH_BUCKET_SIZE,
2718c2ecf20Sopenharmony_ci				       &mvi->bulk_buffer_dma, GFP_KERNEL);
2728c2ecf20Sopenharmony_ci	if (!mvi->bulk_buffer)
2738c2ecf20Sopenharmony_ci		goto err_out;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	mvi->bulk_buffer1 = dma_alloc_coherent(mvi->dev,
2768c2ecf20Sopenharmony_ci				       TRASH_BUCKET_SIZE,
2778c2ecf20Sopenharmony_ci				       &mvi->bulk_buffer_dma1, GFP_KERNEL);
2788c2ecf20Sopenharmony_ci	if (!mvi->bulk_buffer1)
2798c2ecf20Sopenharmony_ci		goto err_out;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	sprintf(pool_name, "%s%d", "mvs_dma_pool", mvi->id);
2828c2ecf20Sopenharmony_ci	mvi->dma_pool = dma_pool_create(pool_name, &mvi->pdev->dev,
2838c2ecf20Sopenharmony_ci					MVS_SLOT_BUF_SZ, 16, 0);
2848c2ecf20Sopenharmony_ci	if (!mvi->dma_pool) {
2858c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "failed to create dma pool %s.\n", pool_name);
2868c2ecf20Sopenharmony_ci			goto err_out;
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci	mvi->tags_num = slot_nr;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	/* Initialize tags */
2918c2ecf20Sopenharmony_ci	mvs_tag_init(mvi);
2928c2ecf20Sopenharmony_ci	return 0;
2938c2ecf20Sopenharmony_cierr_out:
2948c2ecf20Sopenharmony_ci	return 1;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ciint mvs_ioremap(struct mvs_info *mvi, int bar, int bar_ex)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	unsigned long res_start, res_len, res_flag_ex = 0;
3018c2ecf20Sopenharmony_ci	struct pci_dev *pdev = mvi->pdev;
3028c2ecf20Sopenharmony_ci	if (bar_ex != -1) {
3038c2ecf20Sopenharmony_ci		/*
3048c2ecf20Sopenharmony_ci		 * ioremap main and peripheral registers
3058c2ecf20Sopenharmony_ci		 */
3068c2ecf20Sopenharmony_ci		res_start = pci_resource_start(pdev, bar_ex);
3078c2ecf20Sopenharmony_ci		res_len = pci_resource_len(pdev, bar_ex);
3088c2ecf20Sopenharmony_ci		if (!res_start || !res_len)
3098c2ecf20Sopenharmony_ci			goto err_out;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		res_flag_ex = pci_resource_flags(pdev, bar_ex);
3128c2ecf20Sopenharmony_ci		if (res_flag_ex & IORESOURCE_MEM)
3138c2ecf20Sopenharmony_ci			mvi->regs_ex = ioremap(res_start, res_len);
3148c2ecf20Sopenharmony_ci		else
3158c2ecf20Sopenharmony_ci			mvi->regs_ex = (void *)res_start;
3168c2ecf20Sopenharmony_ci		if (!mvi->regs_ex)
3178c2ecf20Sopenharmony_ci			goto err_out;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	res_start = pci_resource_start(pdev, bar);
3218c2ecf20Sopenharmony_ci	res_len = pci_resource_len(pdev, bar);
3228c2ecf20Sopenharmony_ci	if (!res_start || !res_len) {
3238c2ecf20Sopenharmony_ci		iounmap(mvi->regs_ex);
3248c2ecf20Sopenharmony_ci		mvi->regs_ex = NULL;
3258c2ecf20Sopenharmony_ci		goto err_out;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	mvi->regs = ioremap(res_start, res_len);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (!mvi->regs) {
3318c2ecf20Sopenharmony_ci		if (mvi->regs_ex && (res_flag_ex & IORESOURCE_MEM))
3328c2ecf20Sopenharmony_ci			iounmap(mvi->regs_ex);
3338c2ecf20Sopenharmony_ci		mvi->regs_ex = NULL;
3348c2ecf20Sopenharmony_ci		goto err_out;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	return 0;
3388c2ecf20Sopenharmony_cierr_out:
3398c2ecf20Sopenharmony_ci	return -1;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_civoid mvs_iounmap(void __iomem *regs)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	iounmap(regs);
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic struct mvs_info *mvs_pci_alloc(struct pci_dev *pdev,
3488c2ecf20Sopenharmony_ci				const struct pci_device_id *ent,
3498c2ecf20Sopenharmony_ci				struct Scsi_Host *shost, unsigned int id)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	struct mvs_info *mvi = NULL;
3528c2ecf20Sopenharmony_ci	struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	mvi = kzalloc(sizeof(*mvi) +
3558c2ecf20Sopenharmony_ci		(1L << mvs_chips[ent->driver_data].slot_width) *
3568c2ecf20Sopenharmony_ci		sizeof(struct mvs_slot_info), GFP_KERNEL);
3578c2ecf20Sopenharmony_ci	if (!mvi)
3588c2ecf20Sopenharmony_ci		return NULL;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	mvi->pdev = pdev;
3618c2ecf20Sopenharmony_ci	mvi->dev = &pdev->dev;
3628c2ecf20Sopenharmony_ci	mvi->chip_id = ent->driver_data;
3638c2ecf20Sopenharmony_ci	mvi->chip = &mvs_chips[mvi->chip_id];
3648c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mvi->wq_list);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	((struct mvs_prv_info *)sha->lldd_ha)->mvi[id] = mvi;
3678c2ecf20Sopenharmony_ci	((struct mvs_prv_info *)sha->lldd_ha)->n_phy = mvi->chip->n_phy;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	mvi->id = id;
3708c2ecf20Sopenharmony_ci	mvi->sas = sha;
3718c2ecf20Sopenharmony_ci	mvi->shost = shost;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	mvi->tags = kzalloc(MVS_CHIP_SLOT_SZ>>3, GFP_KERNEL);
3748c2ecf20Sopenharmony_ci	if (!mvi->tags)
3758c2ecf20Sopenharmony_ci		goto err_out;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (MVS_CHIP_DISP->chip_ioremap(mvi))
3788c2ecf20Sopenharmony_ci		goto err_out;
3798c2ecf20Sopenharmony_ci	if (!mvs_alloc(mvi, shost))
3808c2ecf20Sopenharmony_ci		return mvi;
3818c2ecf20Sopenharmony_cierr_out:
3828c2ecf20Sopenharmony_ci	mvs_free(mvi);
3838c2ecf20Sopenharmony_ci	return NULL;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic int pci_go_64(struct pci_dev *pdev)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	int rc;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
3918c2ecf20Sopenharmony_ci	if (rc) {
3928c2ecf20Sopenharmony_ci		rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
3938c2ecf20Sopenharmony_ci		if (rc) {
3948c2ecf20Sopenharmony_ci			dev_printk(KERN_ERR, &pdev->dev,
3958c2ecf20Sopenharmony_ci				   "32-bit DMA enable failed\n");
3968c2ecf20Sopenharmony_ci			return rc;
3978c2ecf20Sopenharmony_ci		}
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	return rc;
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic int mvs_prep_sas_ha_init(struct Scsi_Host *shost,
4048c2ecf20Sopenharmony_ci				const struct mvs_chip_info *chip_info)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	int phy_nr, port_nr; unsigned short core_nr;
4078c2ecf20Sopenharmony_ci	struct asd_sas_phy **arr_phy;
4088c2ecf20Sopenharmony_ci	struct asd_sas_port **arr_port;
4098c2ecf20Sopenharmony_ci	struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	core_nr = chip_info->n_host;
4128c2ecf20Sopenharmony_ci	phy_nr  = core_nr * chip_info->n_phy;
4138c2ecf20Sopenharmony_ci	port_nr = phy_nr;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	memset(sha, 0x00, sizeof(struct sas_ha_struct));
4168c2ecf20Sopenharmony_ci	arr_phy  = kcalloc(phy_nr, sizeof(void *), GFP_KERNEL);
4178c2ecf20Sopenharmony_ci	arr_port = kcalloc(port_nr, sizeof(void *), GFP_KERNEL);
4188c2ecf20Sopenharmony_ci	if (!arr_phy || !arr_port)
4198c2ecf20Sopenharmony_ci		goto exit_free;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	sha->sas_phy = arr_phy;
4228c2ecf20Sopenharmony_ci	sha->sas_port = arr_port;
4238c2ecf20Sopenharmony_ci	sha->core.shost = shost;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	sha->lldd_ha = kzalloc(sizeof(struct mvs_prv_info), GFP_KERNEL);
4268c2ecf20Sopenharmony_ci	if (!sha->lldd_ha)
4278c2ecf20Sopenharmony_ci		goto exit_free;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	((struct mvs_prv_info *)sha->lldd_ha)->n_host = core_nr;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	shost->transportt = mvs_stt;
4328c2ecf20Sopenharmony_ci	shost->max_id = MVS_MAX_DEVICES;
4338c2ecf20Sopenharmony_ci	shost->max_lun = ~0;
4348c2ecf20Sopenharmony_ci	shost->max_channel = 1;
4358c2ecf20Sopenharmony_ci	shost->max_cmd_len = 16;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	return 0;
4388c2ecf20Sopenharmony_ciexit_free:
4398c2ecf20Sopenharmony_ci	kfree(arr_phy);
4408c2ecf20Sopenharmony_ci	kfree(arr_port);
4418c2ecf20Sopenharmony_ci	return -1;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistatic void  mvs_post_sas_ha_init(struct Scsi_Host *shost,
4468c2ecf20Sopenharmony_ci			const struct mvs_chip_info *chip_info)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	int can_queue, i = 0, j = 0;
4498c2ecf20Sopenharmony_ci	struct mvs_info *mvi = NULL;
4508c2ecf20Sopenharmony_ci	struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
4518c2ecf20Sopenharmony_ci	unsigned short nr_core = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	for (j = 0; j < nr_core; j++) {
4548c2ecf20Sopenharmony_ci		mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[j];
4558c2ecf20Sopenharmony_ci		for (i = 0; i < chip_info->n_phy; i++) {
4568c2ecf20Sopenharmony_ci			sha->sas_phy[j * chip_info->n_phy  + i] =
4578c2ecf20Sopenharmony_ci				&mvi->phy[i].sas_phy;
4588c2ecf20Sopenharmony_ci			sha->sas_port[j * chip_info->n_phy + i] =
4598c2ecf20Sopenharmony_ci				&mvi->port[i].sas_port;
4608c2ecf20Sopenharmony_ci		}
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	sha->sas_ha_name = DRV_NAME;
4648c2ecf20Sopenharmony_ci	sha->dev = mvi->dev;
4658c2ecf20Sopenharmony_ci	sha->lldd_module = THIS_MODULE;
4668c2ecf20Sopenharmony_ci	sha->sas_addr = &mvi->sas_addr[0];
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	sha->num_phys = nr_core * chip_info->n_phy;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (mvi->flags & MVF_FLAG_SOC)
4718c2ecf20Sopenharmony_ci		can_queue = MVS_SOC_CAN_QUEUE;
4728c2ecf20Sopenharmony_ci	else
4738c2ecf20Sopenharmony_ci		can_queue = MVS_CHIP_SLOT_SZ;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	shost->sg_tablesize = min_t(u16, SG_ALL, MVS_MAX_SG);
4768c2ecf20Sopenharmony_ci	shost->can_queue = can_queue;
4778c2ecf20Sopenharmony_ci	mvi->shost->cmd_per_lun = MVS_QUEUE_SIZE;
4788c2ecf20Sopenharmony_ci	sha->core.shost = mvi->shost;
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic void mvs_init_sas_add(struct mvs_info *mvi)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	u8 i;
4848c2ecf20Sopenharmony_ci	for (i = 0; i < mvi->chip->n_phy; i++) {
4858c2ecf20Sopenharmony_ci		mvi->phy[i].dev_sas_addr = 0x5005043011ab0000ULL;
4868c2ecf20Sopenharmony_ci		mvi->phy[i].dev_sas_addr =
4878c2ecf20Sopenharmony_ci			cpu_to_be64((u64)(*(u64 *)&mvi->phy[i].dev_sas_addr));
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	memcpy(mvi->sas_addr, &mvi->phy[0].dev_sas_addr, SAS_ADDR_SIZE);
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cistatic int mvs_pci_init(struct pci_dev *pdev, const struct pci_device_id *ent)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	unsigned int rc, nhost = 0;
4968c2ecf20Sopenharmony_ci	struct mvs_info *mvi;
4978c2ecf20Sopenharmony_ci	struct mvs_prv_info *mpi;
4988c2ecf20Sopenharmony_ci	irq_handler_t irq_handler = mvs_interrupt;
4998c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = NULL;
5008c2ecf20Sopenharmony_ci	const struct mvs_chip_info *chip;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	dev_printk(KERN_INFO, &pdev->dev,
5038c2ecf20Sopenharmony_ci		"mvsas: driver version %s\n", DRV_VERSION);
5048c2ecf20Sopenharmony_ci	rc = pci_enable_device(pdev);
5058c2ecf20Sopenharmony_ci	if (rc)
5068c2ecf20Sopenharmony_ci		goto err_out_enable;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	pci_set_master(pdev);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	rc = pci_request_regions(pdev, DRV_NAME);
5118c2ecf20Sopenharmony_ci	if (rc)
5128c2ecf20Sopenharmony_ci		goto err_out_disable;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	rc = pci_go_64(pdev);
5158c2ecf20Sopenharmony_ci	if (rc)
5168c2ecf20Sopenharmony_ci		goto err_out_regions;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	shost = scsi_host_alloc(&mvs_sht, sizeof(void *));
5198c2ecf20Sopenharmony_ci	if (!shost) {
5208c2ecf20Sopenharmony_ci		rc = -ENOMEM;
5218c2ecf20Sopenharmony_ci		goto err_out_regions;
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	chip = &mvs_chips[ent->driver_data];
5258c2ecf20Sopenharmony_ci	SHOST_TO_SAS_HA(shost) =
5268c2ecf20Sopenharmony_ci		kcalloc(1, sizeof(struct sas_ha_struct), GFP_KERNEL);
5278c2ecf20Sopenharmony_ci	if (!SHOST_TO_SAS_HA(shost)) {
5288c2ecf20Sopenharmony_ci		scsi_host_put(shost);
5298c2ecf20Sopenharmony_ci		rc = -ENOMEM;
5308c2ecf20Sopenharmony_ci		goto err_out_regions;
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	rc = mvs_prep_sas_ha_init(shost, chip);
5348c2ecf20Sopenharmony_ci	if (rc) {
5358c2ecf20Sopenharmony_ci		scsi_host_put(shost);
5368c2ecf20Sopenharmony_ci		rc = -ENOMEM;
5378c2ecf20Sopenharmony_ci		goto err_out_regions;
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, SHOST_TO_SAS_HA(shost));
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	do {
5438c2ecf20Sopenharmony_ci		mvi = mvs_pci_alloc(pdev, ent, shost, nhost);
5448c2ecf20Sopenharmony_ci		if (!mvi) {
5458c2ecf20Sopenharmony_ci			rc = -ENOMEM;
5468c2ecf20Sopenharmony_ci			goto err_out_regions;
5478c2ecf20Sopenharmony_ci		}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci		memset(&mvi->hba_info_param, 0xFF,
5508c2ecf20Sopenharmony_ci			sizeof(struct hba_info_page));
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci		mvs_init_sas_add(mvi);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci		mvi->instance = nhost;
5558c2ecf20Sopenharmony_ci		rc = MVS_CHIP_DISP->chip_init(mvi);
5568c2ecf20Sopenharmony_ci		if (rc) {
5578c2ecf20Sopenharmony_ci			mvs_free(mvi);
5588c2ecf20Sopenharmony_ci			goto err_out_regions;
5598c2ecf20Sopenharmony_ci		}
5608c2ecf20Sopenharmony_ci		nhost++;
5618c2ecf20Sopenharmony_ci	} while (nhost < chip->n_host);
5628c2ecf20Sopenharmony_ci	mpi = (struct mvs_prv_info *)(SHOST_TO_SAS_HA(shost)->lldd_ha);
5638c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_MVSAS_TASKLET
5648c2ecf20Sopenharmony_ci	tasklet_init(&(mpi->mv_tasklet), mvs_tasklet,
5658c2ecf20Sopenharmony_ci		     (unsigned long)SHOST_TO_SAS_HA(shost));
5668c2ecf20Sopenharmony_ci#endif
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	mvs_post_sas_ha_init(shost, chip);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	rc = scsi_add_host(shost, &pdev->dev);
5718c2ecf20Sopenharmony_ci	if (rc)
5728c2ecf20Sopenharmony_ci		goto err_out_shost;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	rc = sas_register_ha(SHOST_TO_SAS_HA(shost));
5758c2ecf20Sopenharmony_ci	if (rc)
5768c2ecf20Sopenharmony_ci		goto err_out_shost;
5778c2ecf20Sopenharmony_ci	rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED,
5788c2ecf20Sopenharmony_ci		DRV_NAME, SHOST_TO_SAS_HA(shost));
5798c2ecf20Sopenharmony_ci	if (rc)
5808c2ecf20Sopenharmony_ci		goto err_not_sas;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	MVS_CHIP_DISP->interrupt_enable(mvi);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	scsi_scan_host(mvi->shost);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	return 0;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cierr_not_sas:
5898c2ecf20Sopenharmony_ci	sas_unregister_ha(SHOST_TO_SAS_HA(shost));
5908c2ecf20Sopenharmony_cierr_out_shost:
5918c2ecf20Sopenharmony_ci	scsi_remove_host(mvi->shost);
5928c2ecf20Sopenharmony_cierr_out_regions:
5938c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
5948c2ecf20Sopenharmony_cierr_out_disable:
5958c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
5968c2ecf20Sopenharmony_cierr_out_enable:
5978c2ecf20Sopenharmony_ci	return rc;
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_cistatic void mvs_pci_remove(struct pci_dev *pdev)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	unsigned short core_nr, i = 0;
6038c2ecf20Sopenharmony_ci	struct sas_ha_struct *sha = pci_get_drvdata(pdev);
6048c2ecf20Sopenharmony_ci	struct mvs_info *mvi = NULL;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
6078c2ecf20Sopenharmony_ci	mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_MVSAS_TASKLET
6108c2ecf20Sopenharmony_ci	tasklet_kill(&((struct mvs_prv_info *)sha->lldd_ha)->mv_tasklet);
6118c2ecf20Sopenharmony_ci#endif
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	sas_unregister_ha(sha);
6148c2ecf20Sopenharmony_ci	sas_remove_host(mvi->shost);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	MVS_CHIP_DISP->interrupt_disable(mvi);
6178c2ecf20Sopenharmony_ci	free_irq(mvi->pdev->irq, sha);
6188c2ecf20Sopenharmony_ci	for (i = 0; i < core_nr; i++) {
6198c2ecf20Sopenharmony_ci		mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
6208c2ecf20Sopenharmony_ci		mvs_free(mvi);
6218c2ecf20Sopenharmony_ci	}
6228c2ecf20Sopenharmony_ci	kfree(sha->sas_phy);
6238c2ecf20Sopenharmony_ci	kfree(sha->sas_port);
6248c2ecf20Sopenharmony_ci	kfree(sha);
6258c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
6268c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
6278c2ecf20Sopenharmony_ci	return;
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_cistatic struct pci_device_id mvs_pci_table[] = {
6318c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x6320), chip_6320 },
6328c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x6340), chip_6440 },
6338c2ecf20Sopenharmony_ci	{
6348c2ecf20Sopenharmony_ci		.vendor 	= PCI_VENDOR_ID_MARVELL,
6358c2ecf20Sopenharmony_ci		.device 	= 0x6440,
6368c2ecf20Sopenharmony_ci		.subvendor	= PCI_ANY_ID,
6378c2ecf20Sopenharmony_ci		.subdevice	= 0x6480,
6388c2ecf20Sopenharmony_ci		.class		= 0,
6398c2ecf20Sopenharmony_ci		.class_mask	= 0,
6408c2ecf20Sopenharmony_ci		.driver_data	= chip_6485,
6418c2ecf20Sopenharmony_ci	},
6428c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x6440), chip_6440 },
6438c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x6485), chip_6485 },
6448c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x9480), chip_9480 },
6458c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x9180), chip_9180 },
6468c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(ARECA, PCI_DEVICE_ID_ARECA_1300), chip_1300 },
6478c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(ARECA, PCI_DEVICE_ID_ARECA_1320), chip_1320 },
6488c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(ADAPTEC2, 0x0450), chip_6440 },
6498c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(TTI, 0x2640), chip_6440 },
6508c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(TTI, 0x2710), chip_9480 },
6518c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(TTI, 0x2720), chip_9480 },
6528c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(TTI, 0x2721), chip_9480 },
6538c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(TTI, 0x2722), chip_9480 },
6548c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(TTI, 0x2740), chip_9480 },
6558c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(TTI, 0x2744), chip_9480 },
6568c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(TTI, 0x2760), chip_9480 },
6578c2ecf20Sopenharmony_ci	{
6588c2ecf20Sopenharmony_ci		.vendor		= PCI_VENDOR_ID_MARVELL_EXT,
6598c2ecf20Sopenharmony_ci		.device		= 0x9480,
6608c2ecf20Sopenharmony_ci		.subvendor	= PCI_ANY_ID,
6618c2ecf20Sopenharmony_ci		.subdevice	= 0x9480,
6628c2ecf20Sopenharmony_ci		.class		= 0,
6638c2ecf20Sopenharmony_ci		.class_mask	= 0,
6648c2ecf20Sopenharmony_ci		.driver_data	= chip_9480,
6658c2ecf20Sopenharmony_ci	},
6668c2ecf20Sopenharmony_ci	{
6678c2ecf20Sopenharmony_ci		.vendor		= PCI_VENDOR_ID_MARVELL_EXT,
6688c2ecf20Sopenharmony_ci		.device		= 0x9445,
6698c2ecf20Sopenharmony_ci		.subvendor	= PCI_ANY_ID,
6708c2ecf20Sopenharmony_ci		.subdevice	= 0x9480,
6718c2ecf20Sopenharmony_ci		.class		= 0,
6728c2ecf20Sopenharmony_ci		.class_mask	= 0,
6738c2ecf20Sopenharmony_ci		.driver_data	= chip_9445,
6748c2ecf20Sopenharmony_ci	},
6758c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL_EXT, 0x9485), chip_9485 }, /* Marvell 9480/9485 (any vendor/model) */
6768c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(OCZ, 0x1021), chip_9485}, /* OCZ RevoDrive3 */
6778c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(OCZ, 0x1022), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
6788c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(OCZ, 0x1040), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
6798c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(OCZ, 0x1041), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
6808c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(OCZ, 0x1042), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
6818c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(OCZ, 0x1043), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
6828c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(OCZ, 0x1044), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
6838c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(OCZ, 0x1080), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
6848c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(OCZ, 0x1083), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
6858c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(OCZ, 0x1084), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	{ }	/* terminate list */
6888c2ecf20Sopenharmony_ci};
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_cistatic struct pci_driver mvs_pci_driver = {
6918c2ecf20Sopenharmony_ci	.name		= DRV_NAME,
6928c2ecf20Sopenharmony_ci	.id_table	= mvs_pci_table,
6938c2ecf20Sopenharmony_ci	.probe		= mvs_pci_init,
6948c2ecf20Sopenharmony_ci	.remove		= mvs_pci_remove,
6958c2ecf20Sopenharmony_ci};
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_cistatic ssize_t
6988c2ecf20Sopenharmony_cimvs_show_driver_version(struct device *cdev,
6998c2ecf20Sopenharmony_ci		struct device_attribute *attr,  char *buffer)
7008c2ecf20Sopenharmony_ci{
7018c2ecf20Sopenharmony_ci	return sysfs_emit(buffer, "%s\n", DRV_VERSION);
7028c2ecf20Sopenharmony_ci}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_cistatic DEVICE_ATTR(driver_version,
7058c2ecf20Sopenharmony_ci			 S_IRUGO,
7068c2ecf20Sopenharmony_ci			 mvs_show_driver_version,
7078c2ecf20Sopenharmony_ci			 NULL);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_cistatic ssize_t
7108c2ecf20Sopenharmony_cimvs_store_interrupt_coalescing(struct device *cdev,
7118c2ecf20Sopenharmony_ci			struct device_attribute *attr,
7128c2ecf20Sopenharmony_ci			const char *buffer, size_t size)
7138c2ecf20Sopenharmony_ci{
7148c2ecf20Sopenharmony_ci	unsigned int val = 0;
7158c2ecf20Sopenharmony_ci	struct mvs_info *mvi = NULL;
7168c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = class_to_shost(cdev);
7178c2ecf20Sopenharmony_ci	struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
7188c2ecf20Sopenharmony_ci	u8 i, core_nr;
7198c2ecf20Sopenharmony_ci	if (buffer == NULL)
7208c2ecf20Sopenharmony_ci		return size;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	if (sscanf(buffer, "%u", &val) != 1)
7238c2ecf20Sopenharmony_ci		return -EINVAL;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	if (val >= 0x10000) {
7268c2ecf20Sopenharmony_ci		mv_dprintk("interrupt coalescing timer %d us is"
7278c2ecf20Sopenharmony_ci			"too long\n", val);
7288c2ecf20Sopenharmony_ci		return strlen(buffer);
7298c2ecf20Sopenharmony_ci	}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	interrupt_coalescing = val;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
7348c2ecf20Sopenharmony_ci	mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	if (unlikely(!mvi))
7378c2ecf20Sopenharmony_ci		return -EINVAL;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	for (i = 0; i < core_nr; i++) {
7408c2ecf20Sopenharmony_ci		mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
7418c2ecf20Sopenharmony_ci		if (MVS_CHIP_DISP->tune_interrupt)
7428c2ecf20Sopenharmony_ci			MVS_CHIP_DISP->tune_interrupt(mvi,
7438c2ecf20Sopenharmony_ci				interrupt_coalescing);
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci	mv_dprintk("set interrupt coalescing time to %d us\n",
7468c2ecf20Sopenharmony_ci		interrupt_coalescing);
7478c2ecf20Sopenharmony_ci	return strlen(buffer);
7488c2ecf20Sopenharmony_ci}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_cistatic ssize_t mvs_show_interrupt_coalescing(struct device *cdev,
7518c2ecf20Sopenharmony_ci			struct device_attribute *attr, char *buffer)
7528c2ecf20Sopenharmony_ci{
7538c2ecf20Sopenharmony_ci	return sysfs_emit(buffer, "%d\n", interrupt_coalescing);
7548c2ecf20Sopenharmony_ci}
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_cistatic DEVICE_ATTR(interrupt_coalescing,
7578c2ecf20Sopenharmony_ci			 S_IRUGO|S_IWUSR,
7588c2ecf20Sopenharmony_ci			 mvs_show_interrupt_coalescing,
7598c2ecf20Sopenharmony_ci			 mvs_store_interrupt_coalescing);
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cistatic int __init mvs_init(void)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	int rc;
7648c2ecf20Sopenharmony_ci	mvs_stt = sas_domain_attach_transport(&mvs_transport_ops);
7658c2ecf20Sopenharmony_ci	if (!mvs_stt)
7668c2ecf20Sopenharmony_ci		return -ENOMEM;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	rc = pci_register_driver(&mvs_pci_driver);
7698c2ecf20Sopenharmony_ci	if (rc)
7708c2ecf20Sopenharmony_ci		goto err_out;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	return 0;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_cierr_out:
7758c2ecf20Sopenharmony_ci	sas_release_transport(mvs_stt);
7768c2ecf20Sopenharmony_ci	return rc;
7778c2ecf20Sopenharmony_ci}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_cistatic void __exit mvs_exit(void)
7808c2ecf20Sopenharmony_ci{
7818c2ecf20Sopenharmony_ci	pci_unregister_driver(&mvs_pci_driver);
7828c2ecf20Sopenharmony_ci	sas_release_transport(mvs_stt);
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_cistatic struct device_attribute *mvst_host_attrs[] = {
7868c2ecf20Sopenharmony_ci	&dev_attr_driver_version,
7878c2ecf20Sopenharmony_ci	&dev_attr_interrupt_coalescing,
7888c2ecf20Sopenharmony_ci	NULL,
7898c2ecf20Sopenharmony_ci};
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_cimodule_init(mvs_init);
7928c2ecf20Sopenharmony_cimodule_exit(mvs_exit);
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jeff Garzik <jgarzik@pobox.com>");
7958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell 88SE6440 SAS/SATA controller driver");
7968c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
7978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
7988c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI
7998c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mvs_pci_table);
8008c2ecf20Sopenharmony_ci#endif
801