162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Marvell 88SE94xx hardware specific
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2007 Red Hat, Inc.
662306a36Sopenharmony_ci * Copyright 2008 Marvell. <kewei@marvell.com>
762306a36Sopenharmony_ci * Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
862306a36Sopenharmony_ci*/
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "mv_sas.h"
1162306a36Sopenharmony_ci#include "mv_94xx.h"
1262306a36Sopenharmony_ci#include "mv_chips.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic void mvs_94xx_detect_porttype(struct mvs_info *mvi, int i)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	u32 reg;
1762306a36Sopenharmony_ci	struct mvs_phy *phy = &mvi->phy[i];
1862306a36Sopenharmony_ci	u32 phy_status;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE3);
2162306a36Sopenharmony_ci	reg = mvs_read_port_vsr_data(mvi, i);
2262306a36Sopenharmony_ci	phy_status = ((reg & 0x3f0000) >> 16) & 0xff;
2362306a36Sopenharmony_ci	phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
2462306a36Sopenharmony_ci	switch (phy_status) {
2562306a36Sopenharmony_ci	case 0x10:
2662306a36Sopenharmony_ci		phy->phy_type |= PORT_TYPE_SAS;
2762306a36Sopenharmony_ci		break;
2862306a36Sopenharmony_ci	case 0x1d:
2962306a36Sopenharmony_ci	default:
3062306a36Sopenharmony_ci		phy->phy_type |= PORT_TYPE_SATA;
3162306a36Sopenharmony_ci		break;
3262306a36Sopenharmony_ci	}
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic void set_phy_tuning(struct mvs_info *mvi, int phy_id,
3662306a36Sopenharmony_ci			   struct phy_tuning phy_tuning)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	u32 tmp, setting_0 = 0, setting_1 = 0;
3962306a36Sopenharmony_ci	u8 i;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	/* Remap information for B0 chip:
4262306a36Sopenharmony_ci	*
4362306a36Sopenharmony_ci	* R0Ch -> R118h[15:0] (Adapted DFE F3 - F5 coefficient)
4462306a36Sopenharmony_ci	* R0Dh -> R118h[31:16] (Generation 1 Setting 0)
4562306a36Sopenharmony_ci	* R0Eh -> R11Ch[15:0]  (Generation 1 Setting 1)
4662306a36Sopenharmony_ci	* R0Fh -> R11Ch[31:16] (Generation 2 Setting 0)
4762306a36Sopenharmony_ci	* R10h -> R120h[15:0]  (Generation 2 Setting 1)
4862306a36Sopenharmony_ci	* R11h -> R120h[31:16] (Generation 3 Setting 0)
4962306a36Sopenharmony_ci	* R12h -> R124h[15:0]  (Generation 3 Setting 1)
5062306a36Sopenharmony_ci	* R13h -> R124h[31:16] (Generation 4 Setting 0 (Reserved))
5162306a36Sopenharmony_ci	*/
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	/* A0 has a different set of registers */
5462306a36Sopenharmony_ci	if (mvi->pdev->revision == VANIR_A0_REV)
5562306a36Sopenharmony_ci		return;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
5862306a36Sopenharmony_ci		/* loop 3 times, set Gen 1, Gen 2, Gen 3 */
5962306a36Sopenharmony_ci		switch (i) {
6062306a36Sopenharmony_ci		case 0:
6162306a36Sopenharmony_ci			setting_0 = GENERATION_1_SETTING;
6262306a36Sopenharmony_ci			setting_1 = GENERATION_1_2_SETTING;
6362306a36Sopenharmony_ci			break;
6462306a36Sopenharmony_ci		case 1:
6562306a36Sopenharmony_ci			setting_0 = GENERATION_1_2_SETTING;
6662306a36Sopenharmony_ci			setting_1 = GENERATION_2_3_SETTING;
6762306a36Sopenharmony_ci			break;
6862306a36Sopenharmony_ci		case 2:
6962306a36Sopenharmony_ci			setting_0 = GENERATION_2_3_SETTING;
7062306a36Sopenharmony_ci			setting_1 = GENERATION_3_4_SETTING;
7162306a36Sopenharmony_ci			break;
7262306a36Sopenharmony_ci		}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		/* Set:
7562306a36Sopenharmony_ci		*
7662306a36Sopenharmony_ci		* Transmitter Emphasis Enable
7762306a36Sopenharmony_ci		* Transmitter Emphasis Amplitude
7862306a36Sopenharmony_ci		* Transmitter Amplitude
7962306a36Sopenharmony_ci		*/
8062306a36Sopenharmony_ci		mvs_write_port_vsr_addr(mvi, phy_id, setting_0);
8162306a36Sopenharmony_ci		tmp = mvs_read_port_vsr_data(mvi, phy_id);
8262306a36Sopenharmony_ci		tmp &= ~(0xFBE << 16);
8362306a36Sopenharmony_ci		tmp |= (((phy_tuning.trans_emp_en << 11) |
8462306a36Sopenharmony_ci			(phy_tuning.trans_emp_amp << 7) |
8562306a36Sopenharmony_ci			(phy_tuning.trans_amp << 1)) << 16);
8662306a36Sopenharmony_ci		mvs_write_port_vsr_data(mvi, phy_id, tmp);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci		/* Set Transmitter Amplitude Adjust */
8962306a36Sopenharmony_ci		mvs_write_port_vsr_addr(mvi, phy_id, setting_1);
9062306a36Sopenharmony_ci		tmp = mvs_read_port_vsr_data(mvi, phy_id);
9162306a36Sopenharmony_ci		tmp &= ~(0xC000);
9262306a36Sopenharmony_ci		tmp |= (phy_tuning.trans_amp_adj << 14);
9362306a36Sopenharmony_ci		mvs_write_port_vsr_data(mvi, phy_id, tmp);
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic void set_phy_ffe_tuning(struct mvs_info *mvi, int phy_id,
9862306a36Sopenharmony_ci			       struct ffe_control ffe)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	u32 tmp;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* Don't run this if A0/B0 */
10362306a36Sopenharmony_ci	if ((mvi->pdev->revision == VANIR_A0_REV)
10462306a36Sopenharmony_ci		|| (mvi->pdev->revision == VANIR_B0_REV))
10562306a36Sopenharmony_ci		return;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* FFE Resistor and Capacitor */
10862306a36Sopenharmony_ci	/* R10Ch DFE Resolution Control/Squelch and FFE Setting
10962306a36Sopenharmony_ci	 *
11062306a36Sopenharmony_ci	 * FFE_FORCE            [7]
11162306a36Sopenharmony_ci	 * FFE_RES_SEL          [6:4]
11262306a36Sopenharmony_ci	 * FFE_CAP_SEL          [3:0]
11362306a36Sopenharmony_ci	 */
11462306a36Sopenharmony_ci	mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_FFE_CONTROL);
11562306a36Sopenharmony_ci	tmp = mvs_read_port_vsr_data(mvi, phy_id);
11662306a36Sopenharmony_ci	tmp &= ~0xFF;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* Read from HBA_Info_Page */
11962306a36Sopenharmony_ci	tmp |= ((0x1 << 7) |
12062306a36Sopenharmony_ci		(ffe.ffe_rss_sel << 4) |
12162306a36Sopenharmony_ci		(ffe.ffe_cap_sel << 0));
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	mvs_write_port_vsr_data(mvi, phy_id, tmp);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* R064h PHY Mode Register 1
12662306a36Sopenharmony_ci	 *
12762306a36Sopenharmony_ci	 * DFE_DIS		18
12862306a36Sopenharmony_ci	 */
12962306a36Sopenharmony_ci	mvs_write_port_vsr_addr(mvi, phy_id, VSR_REF_CLOCK_CRTL);
13062306a36Sopenharmony_ci	tmp = mvs_read_port_vsr_data(mvi, phy_id);
13162306a36Sopenharmony_ci	tmp &= ~0x40001;
13262306a36Sopenharmony_ci	/* Hard coding */
13362306a36Sopenharmony_ci	/* No defines in HBA_Info_Page */
13462306a36Sopenharmony_ci	tmp |= (0 << 18);
13562306a36Sopenharmony_ci	mvs_write_port_vsr_data(mvi, phy_id, tmp);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* R110h DFE F0-F1 Coefficient Control/DFE Update Control
13862306a36Sopenharmony_ci	 *
13962306a36Sopenharmony_ci	 * DFE_UPDATE_EN        [11:6]
14062306a36Sopenharmony_ci	 * DFE_FX_FORCE         [5:0]
14162306a36Sopenharmony_ci	 */
14262306a36Sopenharmony_ci	mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_DFE_UPDATE_CRTL);
14362306a36Sopenharmony_ci	tmp = mvs_read_port_vsr_data(mvi, phy_id);
14462306a36Sopenharmony_ci	tmp &= ~0xFFF;
14562306a36Sopenharmony_ci	/* Hard coding */
14662306a36Sopenharmony_ci	/* No defines in HBA_Info_Page */
14762306a36Sopenharmony_ci	tmp |= ((0x3F << 6) | (0x0 << 0));
14862306a36Sopenharmony_ci	mvs_write_port_vsr_data(mvi, phy_id, tmp);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* R1A0h Interface and Digital Reference Clock Control/Reserved_50h
15162306a36Sopenharmony_ci	 *
15262306a36Sopenharmony_ci	 * FFE_TRAIN_EN         3
15362306a36Sopenharmony_ci	 */
15462306a36Sopenharmony_ci	mvs_write_port_vsr_addr(mvi, phy_id, VSR_REF_CLOCK_CRTL);
15562306a36Sopenharmony_ci	tmp = mvs_read_port_vsr_data(mvi, phy_id);
15662306a36Sopenharmony_ci	tmp &= ~0x8;
15762306a36Sopenharmony_ci	/* Hard coding */
15862306a36Sopenharmony_ci	/* No defines in HBA_Info_Page */
15962306a36Sopenharmony_ci	tmp |= (0 << 3);
16062306a36Sopenharmony_ci	mvs_write_port_vsr_data(mvi, phy_id, tmp);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/*Notice: this function must be called when phy is disabled*/
16462306a36Sopenharmony_cistatic void set_phy_rate(struct mvs_info *mvi, int phy_id, u8 rate)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	union reg_phy_cfg phy_cfg, phy_cfg_tmp;
16762306a36Sopenharmony_ci	mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2);
16862306a36Sopenharmony_ci	phy_cfg_tmp.v = mvs_read_port_vsr_data(mvi, phy_id);
16962306a36Sopenharmony_ci	phy_cfg.v = 0;
17062306a36Sopenharmony_ci	phy_cfg.u.disable_phy = phy_cfg_tmp.u.disable_phy;
17162306a36Sopenharmony_ci	phy_cfg.u.sas_support = 1;
17262306a36Sopenharmony_ci	phy_cfg.u.sata_support = 1;
17362306a36Sopenharmony_ci	phy_cfg.u.sata_host_mode = 1;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	switch (rate) {
17662306a36Sopenharmony_ci	case 0x0:
17762306a36Sopenharmony_ci		/* support 1.5 Gbps */
17862306a36Sopenharmony_ci		phy_cfg.u.speed_support = 1;
17962306a36Sopenharmony_ci		phy_cfg.u.snw_3_support = 0;
18062306a36Sopenharmony_ci		phy_cfg.u.tx_lnk_parity = 1;
18162306a36Sopenharmony_ci		phy_cfg.u.tx_spt_phs_lnk_rate = 0x30;
18262306a36Sopenharmony_ci		break;
18362306a36Sopenharmony_ci	case 0x1:
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci		/* support 1.5, 3.0 Gbps */
18662306a36Sopenharmony_ci		phy_cfg.u.speed_support = 3;
18762306a36Sopenharmony_ci		phy_cfg.u.tx_spt_phs_lnk_rate = 0x3c;
18862306a36Sopenharmony_ci		phy_cfg.u.tx_lgcl_lnk_rate = 0x08;
18962306a36Sopenharmony_ci		break;
19062306a36Sopenharmony_ci	case 0x2:
19162306a36Sopenharmony_ci	default:
19262306a36Sopenharmony_ci		/* support 1.5, 3.0, 6.0 Gbps */
19362306a36Sopenharmony_ci		phy_cfg.u.speed_support = 7;
19462306a36Sopenharmony_ci		phy_cfg.u.snw_3_support = 1;
19562306a36Sopenharmony_ci		phy_cfg.u.tx_lnk_parity = 1;
19662306a36Sopenharmony_ci		phy_cfg.u.tx_spt_phs_lnk_rate = 0x3f;
19762306a36Sopenharmony_ci		phy_cfg.u.tx_lgcl_lnk_rate = 0x09;
19862306a36Sopenharmony_ci		break;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci	mvs_write_port_vsr_data(mvi, phy_id, phy_cfg.v);
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic void mvs_94xx_config_reg_from_hba(struct mvs_info *mvi, int phy_id)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	u32 temp;
20662306a36Sopenharmony_ci	temp = (u32)(*(u32 *)&mvi->hba_info_param.phy_tuning[phy_id]);
20762306a36Sopenharmony_ci	if (temp == 0xFFFFFFFFL) {
20862306a36Sopenharmony_ci		mvi->hba_info_param.phy_tuning[phy_id].trans_emp_amp = 0x6;
20962306a36Sopenharmony_ci		mvi->hba_info_param.phy_tuning[phy_id].trans_amp = 0x1A;
21062306a36Sopenharmony_ci		mvi->hba_info_param.phy_tuning[phy_id].trans_amp_adj = 0x3;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	temp = (u8)(*(u8 *)&mvi->hba_info_param.ffe_ctl[phy_id]);
21462306a36Sopenharmony_ci	if (temp == 0xFFL) {
21562306a36Sopenharmony_ci		switch (mvi->pdev->revision) {
21662306a36Sopenharmony_ci		case VANIR_A0_REV:
21762306a36Sopenharmony_ci		case VANIR_B0_REV:
21862306a36Sopenharmony_ci			mvi->hba_info_param.ffe_ctl[phy_id].ffe_rss_sel = 0x7;
21962306a36Sopenharmony_ci			mvi->hba_info_param.ffe_ctl[phy_id].ffe_cap_sel = 0x7;
22062306a36Sopenharmony_ci			break;
22162306a36Sopenharmony_ci		case VANIR_C0_REV:
22262306a36Sopenharmony_ci		case VANIR_C1_REV:
22362306a36Sopenharmony_ci		case VANIR_C2_REV:
22462306a36Sopenharmony_ci		default:
22562306a36Sopenharmony_ci			mvi->hba_info_param.ffe_ctl[phy_id].ffe_rss_sel = 0x7;
22662306a36Sopenharmony_ci			mvi->hba_info_param.ffe_ctl[phy_id].ffe_cap_sel = 0xC;
22762306a36Sopenharmony_ci			break;
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	temp = (u8)(*(u8 *)&mvi->hba_info_param.phy_rate[phy_id]);
23262306a36Sopenharmony_ci	if (temp == 0xFFL)
23362306a36Sopenharmony_ci		/*set default phy_rate = 6Gbps*/
23462306a36Sopenharmony_ci		mvi->hba_info_param.phy_rate[phy_id] = 0x2;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	set_phy_tuning(mvi, phy_id,
23762306a36Sopenharmony_ci		mvi->hba_info_param.phy_tuning[phy_id]);
23862306a36Sopenharmony_ci	set_phy_ffe_tuning(mvi, phy_id,
23962306a36Sopenharmony_ci		mvi->hba_info_param.ffe_ctl[phy_id]);
24062306a36Sopenharmony_ci	set_phy_rate(mvi, phy_id,
24162306a36Sopenharmony_ci		mvi->hba_info_param.phy_rate[phy_id]);
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic void mvs_94xx_enable_xmt(struct mvs_info *mvi, int phy_id)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	void __iomem *regs = mvi->regs;
24762306a36Sopenharmony_ci	u32 tmp;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	tmp = mr32(MVS_PCS);
25062306a36Sopenharmony_ci	tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT2);
25162306a36Sopenharmony_ci	mw32(MVS_PCS, tmp);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic void mvs_94xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	u32 tmp;
25762306a36Sopenharmony_ci	u32 delay = 5000;
25862306a36Sopenharmony_ci	if (hard == MVS_PHY_TUNE) {
25962306a36Sopenharmony_ci		mvs_write_port_cfg_addr(mvi, phy_id, PHYR_SATA_CTL);
26062306a36Sopenharmony_ci		tmp = mvs_read_port_cfg_data(mvi, phy_id);
26162306a36Sopenharmony_ci		mvs_write_port_cfg_data(mvi, phy_id, tmp|0x20000000);
26262306a36Sopenharmony_ci		mvs_write_port_cfg_data(mvi, phy_id, tmp|0x100000);
26362306a36Sopenharmony_ci		return;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci	tmp = mvs_read_port_irq_stat(mvi, phy_id);
26662306a36Sopenharmony_ci	tmp &= ~PHYEV_RDY_CH;
26762306a36Sopenharmony_ci	mvs_write_port_irq_stat(mvi, phy_id, tmp);
26862306a36Sopenharmony_ci	if (hard) {
26962306a36Sopenharmony_ci		tmp = mvs_read_phy_ctl(mvi, phy_id);
27062306a36Sopenharmony_ci		tmp |= PHY_RST_HARD;
27162306a36Sopenharmony_ci		mvs_write_phy_ctl(mvi, phy_id, tmp);
27262306a36Sopenharmony_ci		do {
27362306a36Sopenharmony_ci			tmp = mvs_read_phy_ctl(mvi, phy_id);
27462306a36Sopenharmony_ci			udelay(10);
27562306a36Sopenharmony_ci			delay--;
27662306a36Sopenharmony_ci		} while ((tmp & PHY_RST_HARD) && delay);
27762306a36Sopenharmony_ci		if (!delay)
27862306a36Sopenharmony_ci			mv_dprintk("phy hard reset failed.\n");
27962306a36Sopenharmony_ci	} else {
28062306a36Sopenharmony_ci		tmp = mvs_read_phy_ctl(mvi, phy_id);
28162306a36Sopenharmony_ci		tmp |= PHY_RST;
28262306a36Sopenharmony_ci		mvs_write_phy_ctl(mvi, phy_id, tmp);
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic void mvs_94xx_phy_disable(struct mvs_info *mvi, u32 phy_id)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	u32 tmp;
28962306a36Sopenharmony_ci	mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2);
29062306a36Sopenharmony_ci	tmp = mvs_read_port_vsr_data(mvi, phy_id);
29162306a36Sopenharmony_ci	mvs_write_port_vsr_data(mvi, phy_id, tmp | 0x00800000);
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic void mvs_94xx_phy_enable(struct mvs_info *mvi, u32 phy_id)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	u32 tmp;
29762306a36Sopenharmony_ci	u8 revision = 0;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	revision = mvi->pdev->revision;
30062306a36Sopenharmony_ci	if (revision == VANIR_A0_REV) {
30162306a36Sopenharmony_ci		mvs_write_port_vsr_addr(mvi, phy_id, CMD_HOST_RD_DATA);
30262306a36Sopenharmony_ci		mvs_write_port_vsr_data(mvi, phy_id, 0x8300ffc1);
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci	if (revision == VANIR_B0_REV) {
30562306a36Sopenharmony_ci		mvs_write_port_vsr_addr(mvi, phy_id, CMD_APP_MEM_CTL);
30662306a36Sopenharmony_ci		mvs_write_port_vsr_data(mvi, phy_id, 0x08001006);
30762306a36Sopenharmony_ci		mvs_write_port_vsr_addr(mvi, phy_id, CMD_HOST_RD_DATA);
30862306a36Sopenharmony_ci		mvs_write_port_vsr_data(mvi, phy_id, 0x0000705f);
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2);
31262306a36Sopenharmony_ci	tmp = mvs_read_port_vsr_data(mvi, phy_id);
31362306a36Sopenharmony_ci	tmp |= bit(0);
31462306a36Sopenharmony_ci	mvs_write_port_vsr_data(mvi, phy_id, tmp & 0xfd7fffff);
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic void mvs_94xx_sgpio_init(struct mvs_info *mvi)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	void __iomem *regs = mvi->regs_ex - 0x10200;
32062306a36Sopenharmony_ci	u32 tmp;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	tmp = mr32(MVS_HST_CHIP_CONFIG);
32362306a36Sopenharmony_ci	tmp |= 0x100;
32462306a36Sopenharmony_ci	mw32(MVS_HST_CHIP_CONFIG, tmp);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	mw32(MVS_SGPIO_CTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
32762306a36Sopenharmony_ci		MVS_SGPIO_CTRL_SDOUT_AUTO << MVS_SGPIO_CTRL_SDOUT_SHIFT);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	mw32(MVS_SGPIO_CFG1 + MVS_SGPIO_HOST_OFFSET * mvi->id,
33062306a36Sopenharmony_ci		8 << MVS_SGPIO_CFG1_LOWA_SHIFT |
33162306a36Sopenharmony_ci		8 << MVS_SGPIO_CFG1_HIA_SHIFT |
33262306a36Sopenharmony_ci		4 << MVS_SGPIO_CFG1_LOWB_SHIFT |
33362306a36Sopenharmony_ci		4 << MVS_SGPIO_CFG1_HIB_SHIFT |
33462306a36Sopenharmony_ci		2 << MVS_SGPIO_CFG1_MAXACTON_SHIFT |
33562306a36Sopenharmony_ci		1 << MVS_SGPIO_CFG1_FORCEACTOFF_SHIFT
33662306a36Sopenharmony_ci	);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	mw32(MVS_SGPIO_CFG2 + MVS_SGPIO_HOST_OFFSET * mvi->id,
33962306a36Sopenharmony_ci		(300000 / 100) << MVS_SGPIO_CFG2_CLK_SHIFT | /* 100kHz clock */
34062306a36Sopenharmony_ci		66 << MVS_SGPIO_CFG2_BLINK_SHIFT /* (66 * 0,121 Hz?)*/
34162306a36Sopenharmony_ci	);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	mw32(MVS_SGPIO_CFG0 + MVS_SGPIO_HOST_OFFSET * mvi->id,
34462306a36Sopenharmony_ci		MVS_SGPIO_CFG0_ENABLE |
34562306a36Sopenharmony_ci		MVS_SGPIO_CFG0_BLINKA |
34662306a36Sopenharmony_ci		MVS_SGPIO_CFG0_BLINKB |
34762306a36Sopenharmony_ci		/* 3*4 data bits / PDU */
34862306a36Sopenharmony_ci		(12 - 1) << MVS_SGPIO_CFG0_AUT_BITLEN_SHIFT
34962306a36Sopenharmony_ci	);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	mw32(MVS_SGPIO_DCTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
35262306a36Sopenharmony_ci		DEFAULT_SGPIO_BITS);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	mw32(MVS_SGPIO_DSRC + MVS_SGPIO_HOST_OFFSET * mvi->id,
35562306a36Sopenharmony_ci		((mvi->id * 4) + 3) << (8 * 3) |
35662306a36Sopenharmony_ci		((mvi->id * 4) + 2) << (8 * 2) |
35762306a36Sopenharmony_ci		((mvi->id * 4) + 1) << (8 * 1) |
35862306a36Sopenharmony_ci		((mvi->id * 4) + 0) << (8 * 0));
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic int mvs_94xx_init(struct mvs_info *mvi)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	void __iomem *regs = mvi->regs;
36562306a36Sopenharmony_ci	int i;
36662306a36Sopenharmony_ci	u32 tmp, cctl;
36762306a36Sopenharmony_ci	u8 revision;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	revision = mvi->pdev->revision;
37062306a36Sopenharmony_ci	mvs_show_pcie_usage(mvi);
37162306a36Sopenharmony_ci	if (mvi->flags & MVF_FLAG_SOC) {
37262306a36Sopenharmony_ci		tmp = mr32(MVS_PHY_CTL);
37362306a36Sopenharmony_ci		tmp &= ~PCTL_PWR_OFF;
37462306a36Sopenharmony_ci		tmp |= PCTL_PHY_DSBL;
37562306a36Sopenharmony_ci		mw32(MVS_PHY_CTL, tmp);
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/* Init Chip */
37962306a36Sopenharmony_ci	/* make sure RST is set; HBA_RST /should/ have done that for us */
38062306a36Sopenharmony_ci	cctl = mr32(MVS_CTL) & 0xFFFF;
38162306a36Sopenharmony_ci	if (cctl & CCTL_RST)
38262306a36Sopenharmony_ci		cctl &= ~CCTL_RST;
38362306a36Sopenharmony_ci	else
38462306a36Sopenharmony_ci		mw32_f(MVS_CTL, cctl | CCTL_RST);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (mvi->flags & MVF_FLAG_SOC) {
38762306a36Sopenharmony_ci		tmp = mr32(MVS_PHY_CTL);
38862306a36Sopenharmony_ci		tmp &= ~PCTL_PWR_OFF;
38962306a36Sopenharmony_ci		tmp |= PCTL_COM_ON;
39062306a36Sopenharmony_ci		tmp &= ~PCTL_PHY_DSBL;
39162306a36Sopenharmony_ci		tmp |= PCTL_LINK_RST;
39262306a36Sopenharmony_ci		mw32(MVS_PHY_CTL, tmp);
39362306a36Sopenharmony_ci		msleep(100);
39462306a36Sopenharmony_ci		tmp &= ~PCTL_LINK_RST;
39562306a36Sopenharmony_ci		mw32(MVS_PHY_CTL, tmp);
39662306a36Sopenharmony_ci		msleep(100);
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/* disable Multiplexing, enable phy implemented */
40062306a36Sopenharmony_ci	mw32(MVS_PORTS_IMP, 0xFF);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (revision == VANIR_A0_REV) {
40362306a36Sopenharmony_ci		mw32(MVS_PA_VSR_ADDR, CMD_CMWK_OOB_DET);
40462306a36Sopenharmony_ci		mw32(MVS_PA_VSR_PORT, 0x00018080);
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci	mw32(MVS_PA_VSR_ADDR, VSR_PHY_MODE2);
40762306a36Sopenharmony_ci	if (revision == VANIR_A0_REV || revision == VANIR_B0_REV)
40862306a36Sopenharmony_ci		/* set 6G/3G/1.5G, multiplexing, without SSC */
40962306a36Sopenharmony_ci		mw32(MVS_PA_VSR_PORT, 0x0084d4fe);
41062306a36Sopenharmony_ci	else
41162306a36Sopenharmony_ci		/* set 6G/3G/1.5G, multiplexing, with and without SSC */
41262306a36Sopenharmony_ci		mw32(MVS_PA_VSR_PORT, 0x0084fffe);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	if (revision == VANIR_B0_REV) {
41562306a36Sopenharmony_ci		mw32(MVS_PA_VSR_ADDR, CMD_APP_MEM_CTL);
41662306a36Sopenharmony_ci		mw32(MVS_PA_VSR_PORT, 0x08001006);
41762306a36Sopenharmony_ci		mw32(MVS_PA_VSR_ADDR, CMD_HOST_RD_DATA);
41862306a36Sopenharmony_ci		mw32(MVS_PA_VSR_PORT, 0x0000705f);
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	/* reset control */
42262306a36Sopenharmony_ci	mw32(MVS_PCS, 0);		/* MVS_PCS */
42362306a36Sopenharmony_ci	mw32(MVS_STP_REG_SET_0, 0);
42462306a36Sopenharmony_ci	mw32(MVS_STP_REG_SET_1, 0);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/* init phys */
42762306a36Sopenharmony_ci	mvs_phy_hacks(mvi);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	/* disable non data frame retry */
43062306a36Sopenharmony_ci	tmp = mvs_cr32(mvi, CMD_SAS_CTL1);
43162306a36Sopenharmony_ci	if ((revision == VANIR_A0_REV) ||
43262306a36Sopenharmony_ci		(revision == VANIR_B0_REV) ||
43362306a36Sopenharmony_ci		(revision == VANIR_C0_REV)) {
43462306a36Sopenharmony_ci		tmp &= ~0xffff;
43562306a36Sopenharmony_ci		tmp |= 0x007f;
43662306a36Sopenharmony_ci		mvs_cw32(mvi, CMD_SAS_CTL1, tmp);
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	/* set LED blink when IO*/
44062306a36Sopenharmony_ci	mw32(MVS_PA_VSR_ADDR, VSR_PHY_ACT_LED);
44162306a36Sopenharmony_ci	tmp = mr32(MVS_PA_VSR_PORT);
44262306a36Sopenharmony_ci	tmp &= 0xFFFF00FF;
44362306a36Sopenharmony_ci	tmp |= 0x00003300;
44462306a36Sopenharmony_ci	mw32(MVS_PA_VSR_PORT, tmp);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	mw32(MVS_CMD_LIST_LO, mvi->slot_dma);
44762306a36Sopenharmony_ci	mw32(MVS_CMD_LIST_HI, (mvi->slot_dma >> 16) >> 16);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	mw32(MVS_RX_FIS_LO, mvi->rx_fis_dma);
45062306a36Sopenharmony_ci	mw32(MVS_RX_FIS_HI, (mvi->rx_fis_dma >> 16) >> 16);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	mw32(MVS_TX_CFG, MVS_CHIP_SLOT_SZ);
45362306a36Sopenharmony_ci	mw32(MVS_TX_LO, mvi->tx_dma);
45462306a36Sopenharmony_ci	mw32(MVS_TX_HI, (mvi->tx_dma >> 16) >> 16);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	mw32(MVS_RX_CFG, MVS_RX_RING_SZ);
45762306a36Sopenharmony_ci	mw32(MVS_RX_LO, mvi->rx_dma);
45862306a36Sopenharmony_ci	mw32(MVS_RX_HI, (mvi->rx_dma >> 16) >> 16);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	for (i = 0; i < mvi->chip->n_phy; i++) {
46162306a36Sopenharmony_ci		mvs_94xx_phy_disable(mvi, i);
46262306a36Sopenharmony_ci		/* set phy local SAS address */
46362306a36Sopenharmony_ci		mvs_set_sas_addr(mvi, i, CONFIG_ID_FRAME3, CONFIG_ID_FRAME4,
46462306a36Sopenharmony_ci						cpu_to_le64(mvi->phy[i].dev_sas_addr));
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		mvs_94xx_enable_xmt(mvi, i);
46762306a36Sopenharmony_ci		mvs_94xx_config_reg_from_hba(mvi, i);
46862306a36Sopenharmony_ci		mvs_94xx_phy_enable(mvi, i);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci		mvs_94xx_phy_reset(mvi, i, PHY_RST_HARD);
47162306a36Sopenharmony_ci		msleep(500);
47262306a36Sopenharmony_ci		mvs_94xx_detect_porttype(mvi, i);
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	if (mvi->flags & MVF_FLAG_SOC) {
47662306a36Sopenharmony_ci		/* set select registers */
47762306a36Sopenharmony_ci		writel(0x0E008000, regs + 0x000);
47862306a36Sopenharmony_ci		writel(0x59000008, regs + 0x004);
47962306a36Sopenharmony_ci		writel(0x20, regs + 0x008);
48062306a36Sopenharmony_ci		writel(0x20, regs + 0x00c);
48162306a36Sopenharmony_ci		writel(0x20, regs + 0x010);
48262306a36Sopenharmony_ci		writel(0x20, regs + 0x014);
48362306a36Sopenharmony_ci		writel(0x20, regs + 0x018);
48462306a36Sopenharmony_ci		writel(0x20, regs + 0x01c);
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci	for (i = 0; i < mvi->chip->n_phy; i++) {
48762306a36Sopenharmony_ci		/* clear phy int status */
48862306a36Sopenharmony_ci		tmp = mvs_read_port_irq_stat(mvi, i);
48962306a36Sopenharmony_ci		tmp &= ~PHYEV_SIG_FIS;
49062306a36Sopenharmony_ci		mvs_write_port_irq_stat(mvi, i, tmp);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci		/* set phy int mask */
49362306a36Sopenharmony_ci		tmp = PHYEV_RDY_CH | PHYEV_BROAD_CH |
49462306a36Sopenharmony_ci			PHYEV_ID_DONE  | PHYEV_DCDR_ERR | PHYEV_CRC_ERR ;
49562306a36Sopenharmony_ci		mvs_write_port_irq_mask(mvi, i, tmp);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		msleep(100);
49862306a36Sopenharmony_ci		mvs_update_phyinfo(mvi, i, 1);
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	/* little endian for open address and command table, etc. */
50262306a36Sopenharmony_ci	cctl = mr32(MVS_CTL);
50362306a36Sopenharmony_ci	cctl |= CCTL_ENDIAN_CMD;
50462306a36Sopenharmony_ci	cctl &= ~CCTL_ENDIAN_OPEN;
50562306a36Sopenharmony_ci	cctl |= CCTL_ENDIAN_RSP;
50662306a36Sopenharmony_ci	mw32_f(MVS_CTL, cctl);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	/* reset CMD queue */
50962306a36Sopenharmony_ci	tmp = mr32(MVS_PCS);
51062306a36Sopenharmony_ci	tmp |= PCS_CMD_RST;
51162306a36Sopenharmony_ci	tmp &= ~PCS_SELF_CLEAR;
51262306a36Sopenharmony_ci	mw32(MVS_PCS, tmp);
51362306a36Sopenharmony_ci	/*
51462306a36Sopenharmony_ci	 * the max count is 0x1ff, while our max slot is 0x200,
51562306a36Sopenharmony_ci	 * it will make count 0.
51662306a36Sopenharmony_ci	 */
51762306a36Sopenharmony_ci	tmp = 0;
51862306a36Sopenharmony_ci	if (MVS_CHIP_SLOT_SZ > 0x1ff)
51962306a36Sopenharmony_ci		mw32(MVS_INT_COAL, 0x1ff | COAL_EN);
52062306a36Sopenharmony_ci	else
52162306a36Sopenharmony_ci		mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ | COAL_EN);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* default interrupt coalescing time is 128us */
52462306a36Sopenharmony_ci	tmp = 0x10000 | interrupt_coalescing;
52562306a36Sopenharmony_ci	mw32(MVS_INT_COAL_TMOUT, tmp);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	/* ladies and gentlemen, start your engines */
52862306a36Sopenharmony_ci	mw32(MVS_TX_CFG, 0);
52962306a36Sopenharmony_ci	mw32(MVS_TX_CFG, MVS_CHIP_SLOT_SZ | TX_EN);
53062306a36Sopenharmony_ci	mw32(MVS_RX_CFG, MVS_RX_RING_SZ | RX_EN);
53162306a36Sopenharmony_ci	/* enable CMD/CMPL_Q/RESP mode */
53262306a36Sopenharmony_ci	mw32(MVS_PCS, PCS_SATA_RETRY_2 | PCS_FIS_RX_EN |
53362306a36Sopenharmony_ci		PCS_CMD_EN | PCS_CMD_STOP_ERR);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* enable completion queue interrupt */
53662306a36Sopenharmony_ci	tmp = (CINT_PORT_MASK | CINT_DONE | CINT_MEM | CINT_SRS | CINT_CI_STOP |
53762306a36Sopenharmony_ci		CINT_DMA_PCIE | CINT_NON_SPEC_NCQ_ERROR);
53862306a36Sopenharmony_ci	tmp |= CINT_PHY_MASK;
53962306a36Sopenharmony_ci	mw32(MVS_INT_MASK, tmp);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	tmp = mvs_cr32(mvi, CMD_LINK_TIMER);
54262306a36Sopenharmony_ci	tmp |= 0xFFFF0000;
54362306a36Sopenharmony_ci	mvs_cw32(mvi, CMD_LINK_TIMER, tmp);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	/* tune STP performance */
54662306a36Sopenharmony_ci	tmp = 0x003F003F;
54762306a36Sopenharmony_ci	mvs_cw32(mvi, CMD_PL_TIMER, tmp);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	/* This can improve expander large block size seq write performance */
55062306a36Sopenharmony_ci	tmp = mvs_cr32(mvi, CMD_PORT_LAYER_TIMER1);
55162306a36Sopenharmony_ci	tmp |= 0xFFFF007F;
55262306a36Sopenharmony_ci	mvs_cw32(mvi, CMD_PORT_LAYER_TIMER1, tmp);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	/* change the connection open-close behavior (bit 9)
55562306a36Sopenharmony_ci	 * set bit8 to 1 for performance tuning */
55662306a36Sopenharmony_ci	tmp = mvs_cr32(mvi, CMD_SL_MODE0);
55762306a36Sopenharmony_ci	tmp |= 0x00000300;
55862306a36Sopenharmony_ci	/* set bit0 to 0 to enable retry for no_dest reject case */
55962306a36Sopenharmony_ci	tmp &= 0xFFFFFFFE;
56062306a36Sopenharmony_ci	mvs_cw32(mvi, CMD_SL_MODE0, tmp);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	/* Enable SRS interrupt */
56362306a36Sopenharmony_ci	mw32(MVS_INT_MASK_SRS_0, 0xFFFF);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	mvs_94xx_sgpio_init(mvi);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	return 0;
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_cistatic int mvs_94xx_ioremap(struct mvs_info *mvi)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	if (!mvs_ioremap(mvi, 2, -1)) {
57362306a36Sopenharmony_ci		mvi->regs_ex = mvi->regs + 0x10200;
57462306a36Sopenharmony_ci		mvi->regs += 0x20000;
57562306a36Sopenharmony_ci		if (mvi->id == 1)
57662306a36Sopenharmony_ci			mvi->regs += 0x4000;
57762306a36Sopenharmony_ci		return 0;
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci	return -1;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic void mvs_94xx_iounmap(struct mvs_info *mvi)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	if (mvi->regs) {
58562306a36Sopenharmony_ci		mvi->regs -= 0x20000;
58662306a36Sopenharmony_ci		if (mvi->id == 1)
58762306a36Sopenharmony_ci			mvi->regs -= 0x4000;
58862306a36Sopenharmony_ci		mvs_iounmap(mvi->regs);
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cistatic void mvs_94xx_interrupt_enable(struct mvs_info *mvi)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	void __iomem *regs = mvi->regs_ex;
59562306a36Sopenharmony_ci	u32 tmp;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	tmp = mr32(MVS_GBL_CTL);
59862306a36Sopenharmony_ci	tmp |= (MVS_IRQ_SAS_A | MVS_IRQ_SAS_B);
59962306a36Sopenharmony_ci	mw32(MVS_GBL_INT_STAT, tmp);
60062306a36Sopenharmony_ci	writel(tmp, regs + 0x0C);
60162306a36Sopenharmony_ci	writel(tmp, regs + 0x10);
60262306a36Sopenharmony_ci	writel(tmp, regs + 0x14);
60362306a36Sopenharmony_ci	writel(tmp, regs + 0x18);
60462306a36Sopenharmony_ci	mw32(MVS_GBL_CTL, tmp);
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic void mvs_94xx_interrupt_disable(struct mvs_info *mvi)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	void __iomem *regs = mvi->regs_ex;
61062306a36Sopenharmony_ci	u32 tmp;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	tmp = mr32(MVS_GBL_CTL);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	tmp &= ~(MVS_IRQ_SAS_A | MVS_IRQ_SAS_B);
61562306a36Sopenharmony_ci	mw32(MVS_GBL_INT_STAT, tmp);
61662306a36Sopenharmony_ci	writel(tmp, regs + 0x0C);
61762306a36Sopenharmony_ci	writel(tmp, regs + 0x10);
61862306a36Sopenharmony_ci	writel(tmp, regs + 0x14);
61962306a36Sopenharmony_ci	writel(tmp, regs + 0x18);
62062306a36Sopenharmony_ci	mw32(MVS_GBL_CTL, tmp);
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic u32 mvs_94xx_isr_status(struct mvs_info *mvi, int irq)
62462306a36Sopenharmony_ci{
62562306a36Sopenharmony_ci	void __iomem *regs = mvi->regs_ex;
62662306a36Sopenharmony_ci	u32 stat = 0;
62762306a36Sopenharmony_ci	if (!(mvi->flags & MVF_FLAG_SOC)) {
62862306a36Sopenharmony_ci		stat = mr32(MVS_GBL_INT_STAT);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci		if (!(stat & (MVS_IRQ_SAS_A | MVS_IRQ_SAS_B)))
63162306a36Sopenharmony_ci			return 0;
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci	return stat;
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cistatic irqreturn_t mvs_94xx_isr(struct mvs_info *mvi, int irq, u32 stat)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	void __iomem *regs = mvi->regs;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	if (((stat & MVS_IRQ_SAS_A) && mvi->id == 0) ||
64162306a36Sopenharmony_ci			((stat & MVS_IRQ_SAS_B) && mvi->id == 1)) {
64262306a36Sopenharmony_ci		mw32_f(MVS_INT_STAT, CINT_DONE);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		spin_lock(&mvi->lock);
64562306a36Sopenharmony_ci		mvs_int_full(mvi);
64662306a36Sopenharmony_ci		spin_unlock(&mvi->lock);
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci	return IRQ_HANDLED;
64962306a36Sopenharmony_ci}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cistatic void mvs_94xx_command_active(struct mvs_info *mvi, u32 slot_idx)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	u32 tmp;
65462306a36Sopenharmony_ci	tmp = mvs_cr32(mvi, MVS_COMMAND_ACTIVE+(slot_idx >> 3));
65562306a36Sopenharmony_ci	if (tmp & 1 << (slot_idx % 32)) {
65662306a36Sopenharmony_ci		mv_printk("command active %08X,  slot [%x].\n", tmp, slot_idx);
65762306a36Sopenharmony_ci		mvs_cw32(mvi, MVS_COMMAND_ACTIVE + (slot_idx >> 3),
65862306a36Sopenharmony_ci			1 << (slot_idx % 32));
65962306a36Sopenharmony_ci		do {
66062306a36Sopenharmony_ci			tmp = mvs_cr32(mvi,
66162306a36Sopenharmony_ci				MVS_COMMAND_ACTIVE + (slot_idx >> 3));
66262306a36Sopenharmony_ci		} while (tmp & 1 << (slot_idx % 32));
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic void
66762306a36Sopenharmony_cimvs_94xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	void __iomem *regs = mvi->regs;
67062306a36Sopenharmony_ci	u32 tmp;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	if (clear_all) {
67362306a36Sopenharmony_ci		tmp = mr32(MVS_INT_STAT_SRS_0);
67462306a36Sopenharmony_ci		if (tmp) {
67562306a36Sopenharmony_ci			mv_dprintk("check SRS 0 %08X.\n", tmp);
67662306a36Sopenharmony_ci			mw32(MVS_INT_STAT_SRS_0, tmp);
67762306a36Sopenharmony_ci		}
67862306a36Sopenharmony_ci		tmp = mr32(MVS_INT_STAT_SRS_1);
67962306a36Sopenharmony_ci		if (tmp) {
68062306a36Sopenharmony_ci			mv_dprintk("check SRS 1 %08X.\n", tmp);
68162306a36Sopenharmony_ci			mw32(MVS_INT_STAT_SRS_1, tmp);
68262306a36Sopenharmony_ci		}
68362306a36Sopenharmony_ci	} else {
68462306a36Sopenharmony_ci		if (reg_set > 31)
68562306a36Sopenharmony_ci			tmp = mr32(MVS_INT_STAT_SRS_1);
68662306a36Sopenharmony_ci		else
68762306a36Sopenharmony_ci			tmp = mr32(MVS_INT_STAT_SRS_0);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		if (tmp & (1 << (reg_set % 32))) {
69062306a36Sopenharmony_ci			mv_dprintk("register set 0x%x was stopped.\n", reg_set);
69162306a36Sopenharmony_ci			if (reg_set > 31)
69262306a36Sopenharmony_ci				mw32(MVS_INT_STAT_SRS_1, 1 << (reg_set % 32));
69362306a36Sopenharmony_ci			else
69462306a36Sopenharmony_ci				mw32(MVS_INT_STAT_SRS_0, 1 << (reg_set % 32));
69562306a36Sopenharmony_ci		}
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic void mvs_94xx_issue_stop(struct mvs_info *mvi, enum mvs_port_type type,
70062306a36Sopenharmony_ci				u32 tfs)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	void __iomem *regs = mvi->regs;
70362306a36Sopenharmony_ci	u32 tmp;
70462306a36Sopenharmony_ci	mvs_94xx_clear_srs_irq(mvi, 0, 1);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	tmp = mr32(MVS_INT_STAT);
70762306a36Sopenharmony_ci	mw32(MVS_INT_STAT, tmp | CINT_CI_STOP);
70862306a36Sopenharmony_ci	tmp = mr32(MVS_PCS) | 0xFF00;
70962306a36Sopenharmony_ci	mw32(MVS_PCS, tmp);
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic void mvs_94xx_non_spec_ncq_error(struct mvs_info *mvi)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	void __iomem *regs = mvi->regs;
71562306a36Sopenharmony_ci	u32 err_0, err_1;
71662306a36Sopenharmony_ci	u8 i;
71762306a36Sopenharmony_ci	struct mvs_device *device;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	err_0 = mr32(MVS_NON_NCQ_ERR_0);
72062306a36Sopenharmony_ci	err_1 = mr32(MVS_NON_NCQ_ERR_1);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	mv_dprintk("non specific ncq error err_0:%x,err_1:%x.\n",
72362306a36Sopenharmony_ci			err_0, err_1);
72462306a36Sopenharmony_ci	for (i = 0; i < 32; i++) {
72562306a36Sopenharmony_ci		if (err_0 & bit(i)) {
72662306a36Sopenharmony_ci			device = mvs_find_dev_by_reg_set(mvi, i);
72762306a36Sopenharmony_ci			if (device)
72862306a36Sopenharmony_ci				mvs_release_task(mvi, device->sas_device);
72962306a36Sopenharmony_ci		}
73062306a36Sopenharmony_ci		if (err_1 & bit(i)) {
73162306a36Sopenharmony_ci			device = mvs_find_dev_by_reg_set(mvi, i+32);
73262306a36Sopenharmony_ci			if (device)
73362306a36Sopenharmony_ci				mvs_release_task(mvi, device->sas_device);
73462306a36Sopenharmony_ci		}
73562306a36Sopenharmony_ci	}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	mw32(MVS_NON_NCQ_ERR_0, err_0);
73862306a36Sopenharmony_ci	mw32(MVS_NON_NCQ_ERR_1, err_1);
73962306a36Sopenharmony_ci}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_cistatic void mvs_94xx_free_reg_set(struct mvs_info *mvi, u8 *tfs)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	void __iomem *regs = mvi->regs;
74462306a36Sopenharmony_ci	u8 reg_set = *tfs;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	if (*tfs == MVS_ID_NOT_MAPPED)
74762306a36Sopenharmony_ci		return;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	mvi->sata_reg_set &= ~bit(reg_set);
75062306a36Sopenharmony_ci	if (reg_set < 32)
75162306a36Sopenharmony_ci		w_reg_set_enable(reg_set, (u32)mvi->sata_reg_set);
75262306a36Sopenharmony_ci	else
75362306a36Sopenharmony_ci		w_reg_set_enable(reg_set, (u32)(mvi->sata_reg_set >> 32));
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	*tfs = MVS_ID_NOT_MAPPED;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	return;
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic u8 mvs_94xx_assign_reg_set(struct mvs_info *mvi, u8 *tfs)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	int i;
76362306a36Sopenharmony_ci	void __iomem *regs = mvi->regs;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	if (*tfs != MVS_ID_NOT_MAPPED)
76662306a36Sopenharmony_ci		return 0;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	i = mv_ffc64(mvi->sata_reg_set);
76962306a36Sopenharmony_ci	if (i >= 32) {
77062306a36Sopenharmony_ci		mvi->sata_reg_set |= bit(i);
77162306a36Sopenharmony_ci		w_reg_set_enable(i, (u32)(mvi->sata_reg_set >> 32));
77262306a36Sopenharmony_ci		*tfs = i;
77362306a36Sopenharmony_ci		return 0;
77462306a36Sopenharmony_ci	} else if (i >= 0) {
77562306a36Sopenharmony_ci		mvi->sata_reg_set |= bit(i);
77662306a36Sopenharmony_ci		w_reg_set_enable(i, (u32)mvi->sata_reg_set);
77762306a36Sopenharmony_ci		*tfs = i;
77862306a36Sopenharmony_ci		return 0;
77962306a36Sopenharmony_ci	}
78062306a36Sopenharmony_ci	return MVS_ID_NOT_MAPPED;
78162306a36Sopenharmony_ci}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_cistatic void mvs_94xx_make_prd(struct scatterlist *scatter, int nr, void *prd)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci	int i;
78662306a36Sopenharmony_ci	struct scatterlist *sg;
78762306a36Sopenharmony_ci	struct mvs_prd *buf_prd = prd;
78862306a36Sopenharmony_ci	struct mvs_prd_imt im_len;
78962306a36Sopenharmony_ci	*(u32 *)&im_len = 0;
79062306a36Sopenharmony_ci	for_each_sg(scatter, sg, nr, i) {
79162306a36Sopenharmony_ci		buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
79262306a36Sopenharmony_ci		im_len.len = sg_dma_len(sg);
79362306a36Sopenharmony_ci		buf_prd->im_len = cpu_to_le32(*(u32 *)&im_len);
79462306a36Sopenharmony_ci		buf_prd++;
79562306a36Sopenharmony_ci	}
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic int mvs_94xx_oob_done(struct mvs_info *mvi, int i)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	u32 phy_st;
80162306a36Sopenharmony_ci	phy_st = mvs_read_phy_ctl(mvi, i);
80262306a36Sopenharmony_ci	if (phy_st & PHY_READY_MASK)
80362306a36Sopenharmony_ci		return 1;
80462306a36Sopenharmony_ci	return 0;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cistatic void mvs_94xx_get_dev_identify_frame(struct mvs_info *mvi, int port_id,
80862306a36Sopenharmony_ci					struct sas_identify_frame *id)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	int i;
81162306a36Sopenharmony_ci	u32 id_frame[7];
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	for (i = 0; i < 7; i++) {
81462306a36Sopenharmony_ci		mvs_write_port_cfg_addr(mvi, port_id,
81562306a36Sopenharmony_ci					CONFIG_ID_FRAME0 + i * 4);
81662306a36Sopenharmony_ci		id_frame[i] = cpu_to_le32(mvs_read_port_cfg_data(mvi, port_id));
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci	memcpy(id, id_frame, 28);
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cistatic void mvs_94xx_get_att_identify_frame(struct mvs_info *mvi, int port_id,
82262306a36Sopenharmony_ci					struct sas_identify_frame *id)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	int i;
82562306a36Sopenharmony_ci	u32 id_frame[7];
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	for (i = 0; i < 7; i++) {
82862306a36Sopenharmony_ci		mvs_write_port_cfg_addr(mvi, port_id,
82962306a36Sopenharmony_ci					CONFIG_ATT_ID_FRAME0 + i * 4);
83062306a36Sopenharmony_ci		id_frame[i] = cpu_to_le32(mvs_read_port_cfg_data(mvi, port_id));
83162306a36Sopenharmony_ci		mv_dprintk("94xx phy %d atta frame %d %x.\n",
83262306a36Sopenharmony_ci			port_id + mvi->id * mvi->chip->n_phy, i, id_frame[i]);
83362306a36Sopenharmony_ci	}
83462306a36Sopenharmony_ci	memcpy(id, id_frame, 28);
83562306a36Sopenharmony_ci}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_cistatic u32 mvs_94xx_make_dev_info(struct sas_identify_frame *id)
83862306a36Sopenharmony_ci{
83962306a36Sopenharmony_ci	u32 att_dev_info = 0;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	att_dev_info |= id->dev_type;
84262306a36Sopenharmony_ci	if (id->stp_iport)
84362306a36Sopenharmony_ci		att_dev_info |= PORT_DEV_STP_INIT;
84462306a36Sopenharmony_ci	if (id->smp_iport)
84562306a36Sopenharmony_ci		att_dev_info |= PORT_DEV_SMP_INIT;
84662306a36Sopenharmony_ci	if (id->ssp_iport)
84762306a36Sopenharmony_ci		att_dev_info |= PORT_DEV_SSP_INIT;
84862306a36Sopenharmony_ci	if (id->stp_tport)
84962306a36Sopenharmony_ci		att_dev_info |= PORT_DEV_STP_TRGT;
85062306a36Sopenharmony_ci	if (id->smp_tport)
85162306a36Sopenharmony_ci		att_dev_info |= PORT_DEV_SMP_TRGT;
85262306a36Sopenharmony_ci	if (id->ssp_tport)
85362306a36Sopenharmony_ci		att_dev_info |= PORT_DEV_SSP_TRGT;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	att_dev_info |= (u32)id->phy_id<<24;
85662306a36Sopenharmony_ci	return att_dev_info;
85762306a36Sopenharmony_ci}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_cistatic u32 mvs_94xx_make_att_info(struct sas_identify_frame *id)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	return mvs_94xx_make_dev_info(id);
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cistatic void mvs_94xx_fix_phy_info(struct mvs_info *mvi, int i,
86562306a36Sopenharmony_ci				struct sas_identify_frame *id)
86662306a36Sopenharmony_ci{
86762306a36Sopenharmony_ci	struct mvs_phy *phy = &mvi->phy[i];
86862306a36Sopenharmony_ci	struct asd_sas_phy *sas_phy = &phy->sas_phy;
86962306a36Sopenharmony_ci	mv_dprintk("get all reg link rate is 0x%x\n", phy->phy_status);
87062306a36Sopenharmony_ci	sas_phy->linkrate =
87162306a36Sopenharmony_ci		(phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >>
87262306a36Sopenharmony_ci			PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET;
87362306a36Sopenharmony_ci	sas_phy->linkrate += 0x8;
87462306a36Sopenharmony_ci	mv_dprintk("get link rate is %d\n", sas_phy->linkrate);
87562306a36Sopenharmony_ci	phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
87662306a36Sopenharmony_ci	phy->maximum_linkrate = SAS_LINK_RATE_6_0_GBPS;
87762306a36Sopenharmony_ci	mvs_94xx_get_dev_identify_frame(mvi, i, id);
87862306a36Sopenharmony_ci	phy->dev_info = mvs_94xx_make_dev_info(id);
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	if (phy->phy_type & PORT_TYPE_SAS) {
88162306a36Sopenharmony_ci		mvs_94xx_get_att_identify_frame(mvi, i, id);
88262306a36Sopenharmony_ci		phy->att_dev_info = mvs_94xx_make_att_info(id);
88362306a36Sopenharmony_ci		phy->att_dev_sas_addr = *(u64 *)id->sas_addr;
88462306a36Sopenharmony_ci	} else {
88562306a36Sopenharmony_ci		phy->att_dev_info = PORT_DEV_STP_TRGT | 1;
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	/* enable spin up bit */
88962306a36Sopenharmony_ci	mvs_write_port_cfg_addr(mvi, i, PHYR_PHY_STAT);
89062306a36Sopenharmony_ci	mvs_write_port_cfg_data(mvi, i, 0x04);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_cistatic void mvs_94xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id,
89562306a36Sopenharmony_ci				       struct sas_phy_linkrates *rates)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci	u32 lrmax = 0;
89862306a36Sopenharmony_ci	u32 tmp;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	tmp = mvs_read_phy_ctl(mvi, phy_id);
90162306a36Sopenharmony_ci	lrmax = (rates->maximum_linkrate - SAS_LINK_RATE_1_5_GBPS) << 12;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	if (lrmax) {
90462306a36Sopenharmony_ci		tmp &= ~(0x3 << 12);
90562306a36Sopenharmony_ci		tmp |= lrmax;
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci	mvs_write_phy_ctl(mvi, phy_id, tmp);
90862306a36Sopenharmony_ci	mvs_94xx_phy_reset(mvi, phy_id, PHY_RST_HARD);
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cistatic void mvs_94xx_clear_active_cmds(struct mvs_info *mvi)
91262306a36Sopenharmony_ci{
91362306a36Sopenharmony_ci	u32 tmp;
91462306a36Sopenharmony_ci	void __iomem *regs = mvi->regs;
91562306a36Sopenharmony_ci	tmp = mr32(MVS_STP_REG_SET_0);
91662306a36Sopenharmony_ci	mw32(MVS_STP_REG_SET_0, 0);
91762306a36Sopenharmony_ci	mw32(MVS_STP_REG_SET_0, tmp);
91862306a36Sopenharmony_ci	tmp = mr32(MVS_STP_REG_SET_1);
91962306a36Sopenharmony_ci	mw32(MVS_STP_REG_SET_1, 0);
92062306a36Sopenharmony_ci	mw32(MVS_STP_REG_SET_1, tmp);
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_cistatic u32 mvs_94xx_spi_read_data(struct mvs_info *mvi)
92562306a36Sopenharmony_ci{
92662306a36Sopenharmony_ci	void __iomem *regs = mvi->regs_ex - 0x10200;
92762306a36Sopenharmony_ci	return mr32(SPI_RD_DATA_REG_94XX);
92862306a36Sopenharmony_ci}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_cistatic void mvs_94xx_spi_write_data(struct mvs_info *mvi, u32 data)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci	void __iomem *regs = mvi->regs_ex - 0x10200;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	mw32(SPI_RD_DATA_REG_94XX, data);
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_cistatic int mvs_94xx_spi_buildcmd(struct mvs_info *mvi,
93962306a36Sopenharmony_ci				 u32      *dwCmd,
94062306a36Sopenharmony_ci				 u8       cmd,
94162306a36Sopenharmony_ci				 u8       read,
94262306a36Sopenharmony_ci				 u8       length,
94362306a36Sopenharmony_ci				 u32      addr
94462306a36Sopenharmony_ci				)
94562306a36Sopenharmony_ci{
94662306a36Sopenharmony_ci	void __iomem *regs = mvi->regs_ex - 0x10200;
94762306a36Sopenharmony_ci	u32  dwTmp;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	dwTmp = ((u32)cmd << 8) | ((u32)length << 4);
95062306a36Sopenharmony_ci	if (read)
95162306a36Sopenharmony_ci		dwTmp |= SPI_CTRL_READ_94XX;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	if (addr != MV_MAX_U32) {
95462306a36Sopenharmony_ci		mw32(SPI_ADDR_REG_94XX, (addr & 0x0003FFFFL));
95562306a36Sopenharmony_ci		dwTmp |= SPI_ADDR_VLD_94XX;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	*dwCmd = dwTmp;
95962306a36Sopenharmony_ci	return 0;
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_cistatic int mvs_94xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	void __iomem *regs = mvi->regs_ex - 0x10200;
96662306a36Sopenharmony_ci	mw32(SPI_CTRL_REG_94XX, cmd | SPI_CTRL_SpiStart_94XX);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	return 0;
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_cistatic int mvs_94xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout)
97262306a36Sopenharmony_ci{
97362306a36Sopenharmony_ci	void __iomem *regs = mvi->regs_ex - 0x10200;
97462306a36Sopenharmony_ci	u32   i, dwTmp;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	for (i = 0; i < timeout; i++) {
97762306a36Sopenharmony_ci		dwTmp = mr32(SPI_CTRL_REG_94XX);
97862306a36Sopenharmony_ci		if (!(dwTmp & SPI_CTRL_SpiStart_94XX))
97962306a36Sopenharmony_ci			return 0;
98062306a36Sopenharmony_ci		msleep(10);
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	return -1;
98462306a36Sopenharmony_ci}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_cistatic void mvs_94xx_fix_dma(struct mvs_info *mvi, u32 phy_mask,
98762306a36Sopenharmony_ci			     int buf_len, int from, void *prd)
98862306a36Sopenharmony_ci{
98962306a36Sopenharmony_ci	int i;
99062306a36Sopenharmony_ci	struct mvs_prd *buf_prd = prd;
99162306a36Sopenharmony_ci	dma_addr_t buf_dma;
99262306a36Sopenharmony_ci	struct mvs_prd_imt im_len;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	*(u32 *)&im_len = 0;
99562306a36Sopenharmony_ci	buf_prd += from;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci#define PRD_CHAINED_ENTRY 0x01
99862306a36Sopenharmony_ci	if ((mvi->pdev->revision == VANIR_A0_REV) ||
99962306a36Sopenharmony_ci			(mvi->pdev->revision == VANIR_B0_REV))
100062306a36Sopenharmony_ci		buf_dma = (phy_mask <= 0x08) ?
100162306a36Sopenharmony_ci				mvi->bulk_buffer_dma : mvi->bulk_buffer_dma1;
100262306a36Sopenharmony_ci	else
100362306a36Sopenharmony_ci		return;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	for (i = from; i < MAX_SG_ENTRY; i++, ++buf_prd) {
100662306a36Sopenharmony_ci		if (i == MAX_SG_ENTRY - 1) {
100762306a36Sopenharmony_ci			buf_prd->addr = cpu_to_le64(virt_to_phys(buf_prd - 1));
100862306a36Sopenharmony_ci			im_len.len = 2;
100962306a36Sopenharmony_ci			im_len.misc_ctl = PRD_CHAINED_ENTRY;
101062306a36Sopenharmony_ci		} else {
101162306a36Sopenharmony_ci			buf_prd->addr = cpu_to_le64(buf_dma);
101262306a36Sopenharmony_ci			im_len.len = buf_len;
101362306a36Sopenharmony_ci		}
101462306a36Sopenharmony_ci		buf_prd->im_len = cpu_to_le32(*(u32 *)&im_len);
101562306a36Sopenharmony_ci	}
101662306a36Sopenharmony_ci}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_cistatic void mvs_94xx_tune_interrupt(struct mvs_info *mvi, u32 time)
101962306a36Sopenharmony_ci{
102062306a36Sopenharmony_ci	void __iomem *regs = mvi->regs;
102162306a36Sopenharmony_ci	u32 tmp = 0;
102262306a36Sopenharmony_ci	/*
102362306a36Sopenharmony_ci	 * the max count is 0x1ff, while our max slot is 0x200,
102462306a36Sopenharmony_ci	 * it will make count 0.
102562306a36Sopenharmony_ci	 */
102662306a36Sopenharmony_ci	if (time == 0) {
102762306a36Sopenharmony_ci		mw32(MVS_INT_COAL, 0);
102862306a36Sopenharmony_ci		mw32(MVS_INT_COAL_TMOUT, 0x10000);
102962306a36Sopenharmony_ci	} else {
103062306a36Sopenharmony_ci		if (MVS_CHIP_SLOT_SZ > 0x1ff)
103162306a36Sopenharmony_ci			mw32(MVS_INT_COAL, 0x1ff|COAL_EN);
103262306a36Sopenharmony_ci		else
103362306a36Sopenharmony_ci			mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ|COAL_EN);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci		tmp = 0x10000 | time;
103662306a36Sopenharmony_ci		mw32(MVS_INT_COAL_TMOUT, tmp);
103762306a36Sopenharmony_ci	}
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_cistatic int mvs_94xx_gpio_write(struct mvs_prv_info *mvs_prv,
104262306a36Sopenharmony_ci			u8 reg_type, u8 reg_index,
104362306a36Sopenharmony_ci			u8 reg_count, u8 *write_data)
104462306a36Sopenharmony_ci{
104562306a36Sopenharmony_ci	int i;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	switch (reg_type) {
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	case SAS_GPIO_REG_TX_GP:
105062306a36Sopenharmony_ci		if (reg_index == 0)
105162306a36Sopenharmony_ci			return -EINVAL;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci		if (reg_count > 1)
105462306a36Sopenharmony_ci			return -EINVAL;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci		if (reg_count == 0)
105762306a36Sopenharmony_ci			return 0;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci		/* maximum supported bits = hosts * 4 drives * 3 bits */
106062306a36Sopenharmony_ci		for (i = 0; i < mvs_prv->n_host * 4 * 3; i++) {
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci			/* select host */
106362306a36Sopenharmony_ci			struct mvs_info *mvi = mvs_prv->mvi[i/(4*3)];
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci			void __iomem *regs = mvi->regs_ex - 0x10200;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci			int drive = (i/3) & (4-1); /* drive number on host */
106862306a36Sopenharmony_ci			int driveshift = drive * 8; /* bit offset of drive */
106962306a36Sopenharmony_ci			u32 block = ioread32be(regs + MVS_SGPIO_DCTRL +
107062306a36Sopenharmony_ci				MVS_SGPIO_HOST_OFFSET * mvi->id);
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci			/*
107362306a36Sopenharmony_ci			* if bit is set then create a mask with the first
107462306a36Sopenharmony_ci			* bit of the drive set in the mask ...
107562306a36Sopenharmony_ci			*/
107662306a36Sopenharmony_ci			u32 bit = get_unaligned_be32(write_data) & (1 << i) ?
107762306a36Sopenharmony_ci				1 << driveshift : 0;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci			/*
108062306a36Sopenharmony_ci			* ... and then shift it to the right position based
108162306a36Sopenharmony_ci			* on the led type (activity/id/fail)
108262306a36Sopenharmony_ci			*/
108362306a36Sopenharmony_ci			switch (i%3) {
108462306a36Sopenharmony_ci			case 0: /* activity */
108562306a36Sopenharmony_ci				block &= ~((0x7 << MVS_SGPIO_DCTRL_ACT_SHIFT)
108662306a36Sopenharmony_ci					<< driveshift);
108762306a36Sopenharmony_ci					/* hardwire activity bit to SOF */
108862306a36Sopenharmony_ci				block |= LED_BLINKA_SOF << (
108962306a36Sopenharmony_ci					MVS_SGPIO_DCTRL_ACT_SHIFT +
109062306a36Sopenharmony_ci					driveshift);
109162306a36Sopenharmony_ci				break;
109262306a36Sopenharmony_ci			case 1: /* id */
109362306a36Sopenharmony_ci				block &= ~((0x3 << MVS_SGPIO_DCTRL_LOC_SHIFT)
109462306a36Sopenharmony_ci					<< driveshift);
109562306a36Sopenharmony_ci				block |= bit << MVS_SGPIO_DCTRL_LOC_SHIFT;
109662306a36Sopenharmony_ci				break;
109762306a36Sopenharmony_ci			case 2: /* fail */
109862306a36Sopenharmony_ci				block &= ~((0x7 << MVS_SGPIO_DCTRL_ERR_SHIFT)
109962306a36Sopenharmony_ci					<< driveshift);
110062306a36Sopenharmony_ci				block |= bit << MVS_SGPIO_DCTRL_ERR_SHIFT;
110162306a36Sopenharmony_ci				break;
110262306a36Sopenharmony_ci			}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci			iowrite32be(block,
110562306a36Sopenharmony_ci				regs + MVS_SGPIO_DCTRL +
110662306a36Sopenharmony_ci				MVS_SGPIO_HOST_OFFSET * mvi->id);
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci		}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci		return reg_count;
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	case SAS_GPIO_REG_TX:
111362306a36Sopenharmony_ci		if (reg_index + reg_count > mvs_prv->n_host)
111462306a36Sopenharmony_ci			return -EINVAL;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci		for (i = 0; i < reg_count; i++) {
111762306a36Sopenharmony_ci			struct mvs_info *mvi = mvs_prv->mvi[i+reg_index];
111862306a36Sopenharmony_ci			void __iomem *regs = mvi->regs_ex - 0x10200;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci			mw32(MVS_SGPIO_DCTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
112162306a36Sopenharmony_ci				((u32 *) write_data)[i]);
112262306a36Sopenharmony_ci		}
112362306a36Sopenharmony_ci		return reg_count;
112462306a36Sopenharmony_ci	}
112562306a36Sopenharmony_ci	return -ENOSYS;
112662306a36Sopenharmony_ci}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ciconst struct mvs_dispatch mvs_94xx_dispatch = {
112962306a36Sopenharmony_ci	"mv94xx",
113062306a36Sopenharmony_ci	mvs_94xx_init,
113162306a36Sopenharmony_ci	NULL,
113262306a36Sopenharmony_ci	mvs_94xx_ioremap,
113362306a36Sopenharmony_ci	mvs_94xx_iounmap,
113462306a36Sopenharmony_ci	mvs_94xx_isr,
113562306a36Sopenharmony_ci	mvs_94xx_isr_status,
113662306a36Sopenharmony_ci	mvs_94xx_interrupt_enable,
113762306a36Sopenharmony_ci	mvs_94xx_interrupt_disable,
113862306a36Sopenharmony_ci	mvs_read_phy_ctl,
113962306a36Sopenharmony_ci	mvs_write_phy_ctl,
114062306a36Sopenharmony_ci	mvs_read_port_cfg_data,
114162306a36Sopenharmony_ci	mvs_write_port_cfg_data,
114262306a36Sopenharmony_ci	mvs_write_port_cfg_addr,
114362306a36Sopenharmony_ci	mvs_read_port_vsr_data,
114462306a36Sopenharmony_ci	mvs_write_port_vsr_data,
114562306a36Sopenharmony_ci	mvs_write_port_vsr_addr,
114662306a36Sopenharmony_ci	mvs_read_port_irq_stat,
114762306a36Sopenharmony_ci	mvs_write_port_irq_stat,
114862306a36Sopenharmony_ci	mvs_read_port_irq_mask,
114962306a36Sopenharmony_ci	mvs_write_port_irq_mask,
115062306a36Sopenharmony_ci	mvs_94xx_command_active,
115162306a36Sopenharmony_ci	mvs_94xx_clear_srs_irq,
115262306a36Sopenharmony_ci	mvs_94xx_issue_stop,
115362306a36Sopenharmony_ci	mvs_start_delivery,
115462306a36Sopenharmony_ci	mvs_rx_update,
115562306a36Sopenharmony_ci	mvs_int_full,
115662306a36Sopenharmony_ci	mvs_94xx_assign_reg_set,
115762306a36Sopenharmony_ci	mvs_94xx_free_reg_set,
115862306a36Sopenharmony_ci	mvs_get_prd_size,
115962306a36Sopenharmony_ci	mvs_get_prd_count,
116062306a36Sopenharmony_ci	mvs_94xx_make_prd,
116162306a36Sopenharmony_ci	mvs_94xx_detect_porttype,
116262306a36Sopenharmony_ci	mvs_94xx_oob_done,
116362306a36Sopenharmony_ci	mvs_94xx_fix_phy_info,
116462306a36Sopenharmony_ci	NULL,
116562306a36Sopenharmony_ci	mvs_94xx_phy_set_link_rate,
116662306a36Sopenharmony_ci	mvs_hw_max_link_rate,
116762306a36Sopenharmony_ci	mvs_94xx_phy_disable,
116862306a36Sopenharmony_ci	mvs_94xx_phy_enable,
116962306a36Sopenharmony_ci	mvs_94xx_phy_reset,
117062306a36Sopenharmony_ci	NULL,
117162306a36Sopenharmony_ci	mvs_94xx_clear_active_cmds,
117262306a36Sopenharmony_ci	mvs_94xx_spi_read_data,
117362306a36Sopenharmony_ci	mvs_94xx_spi_write_data,
117462306a36Sopenharmony_ci	mvs_94xx_spi_buildcmd,
117562306a36Sopenharmony_ci	mvs_94xx_spi_issuecmd,
117662306a36Sopenharmony_ci	mvs_94xx_spi_waitdataready,
117762306a36Sopenharmony_ci	mvs_94xx_fix_dma,
117862306a36Sopenharmony_ci	mvs_94xx_tune_interrupt,
117962306a36Sopenharmony_ci	mvs_94xx_non_spec_ncq_error,
118062306a36Sopenharmony_ci	mvs_94xx_gpio_write,
118162306a36Sopenharmony_ci};
118262306a36Sopenharmony_ci
1183