xref: /openbmc/linux/drivers/scsi/mvsas/mv_94xx.c (revision 873e65bc)
1873e65bcSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
220b09c29SAndy Yan /*
320b09c29SAndy Yan  * Marvell 88SE94xx hardware specific
420b09c29SAndy Yan  *
520b09c29SAndy Yan  * Copyright 2007 Red Hat, Inc.
620b09c29SAndy Yan  * Copyright 2008 Marvell. <kewei@marvell.com>
70b15fb1fSXiangliang Yu  * Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
820b09c29SAndy Yan */
920b09c29SAndy Yan 
1020b09c29SAndy Yan #include "mv_sas.h"
1120b09c29SAndy Yan #include "mv_94xx.h"
1220b09c29SAndy Yan #include "mv_chips.h"
1320b09c29SAndy Yan 
mvs_94xx_detect_porttype(struct mvs_info * mvi,int i)1420b09c29SAndy Yan static void mvs_94xx_detect_porttype(struct mvs_info *mvi, int i)
1520b09c29SAndy Yan {
1620b09c29SAndy Yan 	u32 reg;
1720b09c29SAndy Yan 	struct mvs_phy *phy = &mvi->phy[i];
1820b09c29SAndy Yan 	u32 phy_status;
1920b09c29SAndy Yan 
2020b09c29SAndy Yan 	mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE3);
2120b09c29SAndy Yan 	reg = mvs_read_port_vsr_data(mvi, i);
2220b09c29SAndy Yan 	phy_status = ((reg & 0x3f0000) >> 16) & 0xff;
2320b09c29SAndy Yan 	phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
2420b09c29SAndy Yan 	switch (phy_status) {
2520b09c29SAndy Yan 	case 0x10:
2620b09c29SAndy Yan 		phy->phy_type |= PORT_TYPE_SAS;
2720b09c29SAndy Yan 		break;
2820b09c29SAndy Yan 	case 0x1d:
2920b09c29SAndy Yan 	default:
3020b09c29SAndy Yan 		phy->phy_type |= PORT_TYPE_SATA;
3120b09c29SAndy Yan 		break;
3220b09c29SAndy Yan 	}
3320b09c29SAndy Yan }
3420b09c29SAndy Yan 
set_phy_tuning(struct mvs_info * mvi,int phy_id,struct phy_tuning phy_tuning)3514bf41dcSBaoyou Xie static void set_phy_tuning(struct mvs_info *mvi, int phy_id,
36f1f82a91SXiangliang Yu 			   struct phy_tuning phy_tuning)
37f1f82a91SXiangliang Yu {
38f1f82a91SXiangliang Yu 	u32 tmp, setting_0 = 0, setting_1 = 0;
39f1f82a91SXiangliang Yu 	u8 i;
40f1f82a91SXiangliang Yu 
41f1f82a91SXiangliang Yu 	/* Remap information for B0 chip:
42f1f82a91SXiangliang Yu 	*
43f1f82a91SXiangliang Yu 	* R0Ch -> R118h[15:0] (Adapted DFE F3 - F5 coefficient)
44f1f82a91SXiangliang Yu 	* R0Dh -> R118h[31:16] (Generation 1 Setting 0)
45f1f82a91SXiangliang Yu 	* R0Eh -> R11Ch[15:0]  (Generation 1 Setting 1)
46f1f82a91SXiangliang Yu 	* R0Fh -> R11Ch[31:16] (Generation 2 Setting 0)
47f1f82a91SXiangliang Yu 	* R10h -> R120h[15:0]  (Generation 2 Setting 1)
48f1f82a91SXiangliang Yu 	* R11h -> R120h[31:16] (Generation 3 Setting 0)
49f1f82a91SXiangliang Yu 	* R12h -> R124h[15:0]  (Generation 3 Setting 1)
50f1f82a91SXiangliang Yu 	* R13h -> R124h[31:16] (Generation 4 Setting 0 (Reserved))
51f1f82a91SXiangliang Yu 	*/
52f1f82a91SXiangliang Yu 
53f1f82a91SXiangliang Yu 	/* A0 has a different set of registers */
54f1f82a91SXiangliang Yu 	if (mvi->pdev->revision == VANIR_A0_REV)
55f1f82a91SXiangliang Yu 		return;
56f1f82a91SXiangliang Yu 
57f1f82a91SXiangliang Yu 	for (i = 0; i < 3; i++) {
58f1f82a91SXiangliang Yu 		/* loop 3 times, set Gen 1, Gen 2, Gen 3 */
59f1f82a91SXiangliang Yu 		switch (i) {
60f1f82a91SXiangliang Yu 		case 0:
61f1f82a91SXiangliang Yu 			setting_0 = GENERATION_1_SETTING;
62f1f82a91SXiangliang Yu 			setting_1 = GENERATION_1_2_SETTING;
63f1f82a91SXiangliang Yu 			break;
64f1f82a91SXiangliang Yu 		case 1:
65f1f82a91SXiangliang Yu 			setting_0 = GENERATION_1_2_SETTING;
66f1f82a91SXiangliang Yu 			setting_1 = GENERATION_2_3_SETTING;
67f1f82a91SXiangliang Yu 			break;
68f1f82a91SXiangliang Yu 		case 2:
69f1f82a91SXiangliang Yu 			setting_0 = GENERATION_2_3_SETTING;
70f1f82a91SXiangliang Yu 			setting_1 = GENERATION_3_4_SETTING;
71f1f82a91SXiangliang Yu 			break;
72f1f82a91SXiangliang Yu 		}
73f1f82a91SXiangliang Yu 
74f1f82a91SXiangliang Yu 		/* Set:
75f1f82a91SXiangliang Yu 		*
76f1f82a91SXiangliang Yu 		* Transmitter Emphasis Enable
77f1f82a91SXiangliang Yu 		* Transmitter Emphasis Amplitude
78f1f82a91SXiangliang Yu 		* Transmitter Amplitude
79f1f82a91SXiangliang Yu 		*/
80f1f82a91SXiangliang Yu 		mvs_write_port_vsr_addr(mvi, phy_id, setting_0);
81f1f82a91SXiangliang Yu 		tmp = mvs_read_port_vsr_data(mvi, phy_id);
82f1f82a91SXiangliang Yu 		tmp &= ~(0xFBE << 16);
83f1f82a91SXiangliang Yu 		tmp |= (((phy_tuning.trans_emp_en << 11) |
84f1f82a91SXiangliang Yu 			(phy_tuning.trans_emp_amp << 7) |
85f1f82a91SXiangliang Yu 			(phy_tuning.trans_amp << 1)) << 16);
86f1f82a91SXiangliang Yu 		mvs_write_port_vsr_data(mvi, phy_id, tmp);
87f1f82a91SXiangliang Yu 
88f1f82a91SXiangliang Yu 		/* Set Transmitter Amplitude Adjust */
89f1f82a91SXiangliang Yu 		mvs_write_port_vsr_addr(mvi, phy_id, setting_1);
90f1f82a91SXiangliang Yu 		tmp = mvs_read_port_vsr_data(mvi, phy_id);
91f1f82a91SXiangliang Yu 		tmp &= ~(0xC000);
92f1f82a91SXiangliang Yu 		tmp |= (phy_tuning.trans_amp_adj << 14);
93f1f82a91SXiangliang Yu 		mvs_write_port_vsr_data(mvi, phy_id, tmp);
94f1f82a91SXiangliang Yu 	}
95f1f82a91SXiangliang Yu }
96f1f82a91SXiangliang Yu 
set_phy_ffe_tuning(struct mvs_info * mvi,int phy_id,struct ffe_control ffe)9714bf41dcSBaoyou Xie static void set_phy_ffe_tuning(struct mvs_info *mvi, int phy_id,
98f1f82a91SXiangliang Yu 			       struct ffe_control ffe)
99f1f82a91SXiangliang Yu {
100f1f82a91SXiangliang Yu 	u32 tmp;
101f1f82a91SXiangliang Yu 
102f1f82a91SXiangliang Yu 	/* Don't run this if A0/B0 */
103f1f82a91SXiangliang Yu 	if ((mvi->pdev->revision == VANIR_A0_REV)
104f1f82a91SXiangliang Yu 		|| (mvi->pdev->revision == VANIR_B0_REV))
105f1f82a91SXiangliang Yu 		return;
106f1f82a91SXiangliang Yu 
107f1f82a91SXiangliang Yu 	/* FFE Resistor and Capacitor */
108f1f82a91SXiangliang Yu 	/* R10Ch DFE Resolution Control/Squelch and FFE Setting
109f1f82a91SXiangliang Yu 	 *
110f1f82a91SXiangliang Yu 	 * FFE_FORCE            [7]
111f1f82a91SXiangliang Yu 	 * FFE_RES_SEL          [6:4]
112f1f82a91SXiangliang Yu 	 * FFE_CAP_SEL          [3:0]
113f1f82a91SXiangliang Yu 	 */
114f1f82a91SXiangliang Yu 	mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_FFE_CONTROL);
115f1f82a91SXiangliang Yu 	tmp = mvs_read_port_vsr_data(mvi, phy_id);
116f1f82a91SXiangliang Yu 	tmp &= ~0xFF;
117f1f82a91SXiangliang Yu 
118f1f82a91SXiangliang Yu 	/* Read from HBA_Info_Page */
119f1f82a91SXiangliang Yu 	tmp |= ((0x1 << 7) |
120f1f82a91SXiangliang Yu 		(ffe.ffe_rss_sel << 4) |
121f1f82a91SXiangliang Yu 		(ffe.ffe_cap_sel << 0));
122f1f82a91SXiangliang Yu 
123f1f82a91SXiangliang Yu 	mvs_write_port_vsr_data(mvi, phy_id, tmp);
124f1f82a91SXiangliang Yu 
125f1f82a91SXiangliang Yu 	/* R064h PHY Mode Register 1
126f1f82a91SXiangliang Yu 	 *
127f1f82a91SXiangliang Yu 	 * DFE_DIS		18
128f1f82a91SXiangliang Yu 	 */
129f1f82a91SXiangliang Yu 	mvs_write_port_vsr_addr(mvi, phy_id, VSR_REF_CLOCK_CRTL);
130f1f82a91SXiangliang Yu 	tmp = mvs_read_port_vsr_data(mvi, phy_id);
131f1f82a91SXiangliang Yu 	tmp &= ~0x40001;
132f1f82a91SXiangliang Yu 	/* Hard coding */
133f1f82a91SXiangliang Yu 	/* No defines in HBA_Info_Page */
134f1f82a91SXiangliang Yu 	tmp |= (0 << 18);
135f1f82a91SXiangliang Yu 	mvs_write_port_vsr_data(mvi, phy_id, tmp);
136f1f82a91SXiangliang Yu 
137f1f82a91SXiangliang Yu 	/* R110h DFE F0-F1 Coefficient Control/DFE Update Control
138f1f82a91SXiangliang Yu 	 *
139f1f82a91SXiangliang Yu 	 * DFE_UPDATE_EN        [11:6]
140f1f82a91SXiangliang Yu 	 * DFE_FX_FORCE         [5:0]
141f1f82a91SXiangliang Yu 	 */
142f1f82a91SXiangliang Yu 	mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_DFE_UPDATE_CRTL);
143f1f82a91SXiangliang Yu 	tmp = mvs_read_port_vsr_data(mvi, phy_id);
144f1f82a91SXiangliang Yu 	tmp &= ~0xFFF;
145f1f82a91SXiangliang Yu 	/* Hard coding */
146f1f82a91SXiangliang Yu 	/* No defines in HBA_Info_Page */
147f1f82a91SXiangliang Yu 	tmp |= ((0x3F << 6) | (0x0 << 0));
148f1f82a91SXiangliang Yu 	mvs_write_port_vsr_data(mvi, phy_id, tmp);
149f1f82a91SXiangliang Yu 
150f1f82a91SXiangliang Yu 	/* R1A0h Interface and Digital Reference Clock Control/Reserved_50h
151f1f82a91SXiangliang Yu 	 *
152f1f82a91SXiangliang Yu 	 * FFE_TRAIN_EN         3
153f1f82a91SXiangliang Yu 	 */
154f1f82a91SXiangliang Yu 	mvs_write_port_vsr_addr(mvi, phy_id, VSR_REF_CLOCK_CRTL);
155f1f82a91SXiangliang Yu 	tmp = mvs_read_port_vsr_data(mvi, phy_id);
156f1f82a91SXiangliang Yu 	tmp &= ~0x8;
157f1f82a91SXiangliang Yu 	/* Hard coding */
158f1f82a91SXiangliang Yu 	/* No defines in HBA_Info_Page */
159f1f82a91SXiangliang Yu 	tmp |= (0 << 3);
160f1f82a91SXiangliang Yu 	mvs_write_port_vsr_data(mvi, phy_id, tmp);
161f1f82a91SXiangliang Yu }
162f1f82a91SXiangliang Yu 
163f1f82a91SXiangliang Yu /*Notice: this function must be called when phy is disabled*/
set_phy_rate(struct mvs_info * mvi,int phy_id,u8 rate)16414bf41dcSBaoyou Xie static void set_phy_rate(struct mvs_info *mvi, int phy_id, u8 rate)
165f1f82a91SXiangliang Yu {
166f1f82a91SXiangliang Yu 	union reg_phy_cfg phy_cfg, phy_cfg_tmp;
167f1f82a91SXiangliang Yu 	mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2);
168f1f82a91SXiangliang Yu 	phy_cfg_tmp.v = mvs_read_port_vsr_data(mvi, phy_id);
169f1f82a91SXiangliang Yu 	phy_cfg.v = 0;
170f1f82a91SXiangliang Yu 	phy_cfg.u.disable_phy = phy_cfg_tmp.u.disable_phy;
171f1f82a91SXiangliang Yu 	phy_cfg.u.sas_support = 1;
172f1f82a91SXiangliang Yu 	phy_cfg.u.sata_support = 1;
173f1f82a91SXiangliang Yu 	phy_cfg.u.sata_host_mode = 1;
174f1f82a91SXiangliang Yu 
175f1f82a91SXiangliang Yu 	switch (rate) {
176f1f82a91SXiangliang Yu 	case 0x0:
177f1f82a91SXiangliang Yu 		/* support 1.5 Gbps */
178f1f82a91SXiangliang Yu 		phy_cfg.u.speed_support = 1;
179f1f82a91SXiangliang Yu 		phy_cfg.u.snw_3_support = 0;
180f1f82a91SXiangliang Yu 		phy_cfg.u.tx_lnk_parity = 1;
181f1f82a91SXiangliang Yu 		phy_cfg.u.tx_spt_phs_lnk_rate = 0x30;
182f1f82a91SXiangliang Yu 		break;
183f1f82a91SXiangliang Yu 	case 0x1:
184f1f82a91SXiangliang Yu 
185f1f82a91SXiangliang Yu 		/* support 1.5, 3.0 Gbps */
186f1f82a91SXiangliang Yu 		phy_cfg.u.speed_support = 3;
187f1f82a91SXiangliang Yu 		phy_cfg.u.tx_spt_phs_lnk_rate = 0x3c;
188f1f82a91SXiangliang Yu 		phy_cfg.u.tx_lgcl_lnk_rate = 0x08;
189f1f82a91SXiangliang Yu 		break;
190f1f82a91SXiangliang Yu 	case 0x2:
191f1f82a91SXiangliang Yu 	default:
192f1f82a91SXiangliang Yu 		/* support 1.5, 3.0, 6.0 Gbps */
193f1f82a91SXiangliang Yu 		phy_cfg.u.speed_support = 7;
194f1f82a91SXiangliang Yu 		phy_cfg.u.snw_3_support = 1;
195f1f82a91SXiangliang Yu 		phy_cfg.u.tx_lnk_parity = 1;
196f1f82a91SXiangliang Yu 		phy_cfg.u.tx_spt_phs_lnk_rate = 0x3f;
197f1f82a91SXiangliang Yu 		phy_cfg.u.tx_lgcl_lnk_rate = 0x09;
198f1f82a91SXiangliang Yu 		break;
199f1f82a91SXiangliang Yu 	}
200f1f82a91SXiangliang Yu 	mvs_write_port_vsr_data(mvi, phy_id, phy_cfg.v);
201f1f82a91SXiangliang Yu }
202f1f82a91SXiangliang Yu 
mvs_94xx_config_reg_from_hba(struct mvs_info * mvi,int phy_id)2036f039790SGreg Kroah-Hartman static void mvs_94xx_config_reg_from_hba(struct mvs_info *mvi, int phy_id)
204f1f82a91SXiangliang Yu {
205f1f82a91SXiangliang Yu 	u32 temp;
206f1f82a91SXiangliang Yu 	temp = (u32)(*(u32 *)&mvi->hba_info_param.phy_tuning[phy_id]);
207f1f82a91SXiangliang Yu 	if (temp == 0xFFFFFFFFL) {
208f1f82a91SXiangliang Yu 		mvi->hba_info_param.phy_tuning[phy_id].trans_emp_amp = 0x6;
209f1f82a91SXiangliang Yu 		mvi->hba_info_param.phy_tuning[phy_id].trans_amp = 0x1A;
210f1f82a91SXiangliang Yu 		mvi->hba_info_param.phy_tuning[phy_id].trans_amp_adj = 0x3;
211f1f82a91SXiangliang Yu 	}
212f1f82a91SXiangliang Yu 
213f1f82a91SXiangliang Yu 	temp = (u8)(*(u8 *)&mvi->hba_info_param.ffe_ctl[phy_id]);
214f1f82a91SXiangliang Yu 	if (temp == 0xFFL) {
215f1f82a91SXiangliang Yu 		switch (mvi->pdev->revision) {
216f1f82a91SXiangliang Yu 		case VANIR_A0_REV:
217f1f82a91SXiangliang Yu 		case VANIR_B0_REV:
218f1f82a91SXiangliang Yu 			mvi->hba_info_param.ffe_ctl[phy_id].ffe_rss_sel = 0x7;
219f1f82a91SXiangliang Yu 			mvi->hba_info_param.ffe_ctl[phy_id].ffe_cap_sel = 0x7;
220f1f82a91SXiangliang Yu 			break;
221f1f82a91SXiangliang Yu 		case VANIR_C0_REV:
222f1f82a91SXiangliang Yu 		case VANIR_C1_REV:
223f1f82a91SXiangliang Yu 		case VANIR_C2_REV:
224f1f82a91SXiangliang Yu 		default:
225f1f82a91SXiangliang Yu 			mvi->hba_info_param.ffe_ctl[phy_id].ffe_rss_sel = 0x7;
226f1f82a91SXiangliang Yu 			mvi->hba_info_param.ffe_ctl[phy_id].ffe_cap_sel = 0xC;
227f1f82a91SXiangliang Yu 			break;
228f1f82a91SXiangliang Yu 		}
229f1f82a91SXiangliang Yu 	}
230f1f82a91SXiangliang Yu 
231f1f82a91SXiangliang Yu 	temp = (u8)(*(u8 *)&mvi->hba_info_param.phy_rate[phy_id]);
232f1f82a91SXiangliang Yu 	if (temp == 0xFFL)
233f1f82a91SXiangliang Yu 		/*set default phy_rate = 6Gbps*/
234f1f82a91SXiangliang Yu 		mvi->hba_info_param.phy_rate[phy_id] = 0x2;
235f1f82a91SXiangliang Yu 
236f1f82a91SXiangliang Yu 	set_phy_tuning(mvi, phy_id,
237f1f82a91SXiangliang Yu 		mvi->hba_info_param.phy_tuning[phy_id]);
238f1f82a91SXiangliang Yu 	set_phy_ffe_tuning(mvi, phy_id,
239f1f82a91SXiangliang Yu 		mvi->hba_info_param.ffe_ctl[phy_id]);
240f1f82a91SXiangliang Yu 	set_phy_rate(mvi, phy_id,
241f1f82a91SXiangliang Yu 		mvi->hba_info_param.phy_rate[phy_id]);
242f1f82a91SXiangliang Yu }
243f1f82a91SXiangliang Yu 
mvs_94xx_enable_xmt(struct mvs_info * mvi,int phy_id)2446f039790SGreg Kroah-Hartman static void mvs_94xx_enable_xmt(struct mvs_info *mvi, int phy_id)
24520b09c29SAndy Yan {
24620b09c29SAndy Yan 	void __iomem *regs = mvi->regs;
24720b09c29SAndy Yan 	u32 tmp;
24820b09c29SAndy Yan 
24920b09c29SAndy Yan 	tmp = mr32(MVS_PCS);
25020b09c29SAndy Yan 	tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT2);
25120b09c29SAndy Yan 	mw32(MVS_PCS, tmp);
25220b09c29SAndy Yan }
25320b09c29SAndy Yan 
mvs_94xx_phy_reset(struct mvs_info * mvi,u32 phy_id,int hard)25420b09c29SAndy Yan static void mvs_94xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard)
25520b09c29SAndy Yan {
25620b09c29SAndy Yan 	u32 tmp;
25784fbd0ceSXiangliang Yu 	u32 delay = 5000;
25884fbd0ceSXiangliang Yu 	if (hard == MVS_PHY_TUNE) {
25984fbd0ceSXiangliang Yu 		mvs_write_port_cfg_addr(mvi, phy_id, PHYR_SATA_CTL);
26084fbd0ceSXiangliang Yu 		tmp = mvs_read_port_cfg_data(mvi, phy_id);
26184fbd0ceSXiangliang Yu 		mvs_write_port_cfg_data(mvi, phy_id, tmp|0x20000000);
26284fbd0ceSXiangliang Yu 		mvs_write_port_cfg_data(mvi, phy_id, tmp|0x100000);
26384fbd0ceSXiangliang Yu 		return;
26484fbd0ceSXiangliang Yu 	}
26520b09c29SAndy Yan 	tmp = mvs_read_port_irq_stat(mvi, phy_id);
26620b09c29SAndy Yan 	tmp &= ~PHYEV_RDY_CH;
26720b09c29SAndy Yan 	mvs_write_port_irq_stat(mvi, phy_id, tmp);
26820b09c29SAndy Yan 	if (hard) {
26920b09c29SAndy Yan 		tmp = mvs_read_phy_ctl(mvi, phy_id);
27020b09c29SAndy Yan 		tmp |= PHY_RST_HARD;
27120b09c29SAndy Yan 		mvs_write_phy_ctl(mvi, phy_id, tmp);
27220b09c29SAndy Yan 		do {
27320b09c29SAndy Yan 			tmp = mvs_read_phy_ctl(mvi, phy_id);
27484fbd0ceSXiangliang Yu 			udelay(10);
27584fbd0ceSXiangliang Yu 			delay--;
27684fbd0ceSXiangliang Yu 		} while ((tmp & PHY_RST_HARD) && delay);
27784fbd0ceSXiangliang Yu 		if (!delay)
27884fbd0ceSXiangliang Yu 			mv_dprintk("phy hard reset failed.\n");
27920b09c29SAndy Yan 	} else {
28084fbd0ceSXiangliang Yu 		tmp = mvs_read_phy_ctl(mvi, phy_id);
28120b09c29SAndy Yan 		tmp |= PHY_RST;
28284fbd0ceSXiangliang Yu 		mvs_write_phy_ctl(mvi, phy_id, tmp);
28320b09c29SAndy Yan 	}
28420b09c29SAndy Yan }
28520b09c29SAndy Yan 
mvs_94xx_phy_disable(struct mvs_info * mvi,u32 phy_id)28620b09c29SAndy Yan static void mvs_94xx_phy_disable(struct mvs_info *mvi, u32 phy_id)
28720b09c29SAndy Yan {
28820b09c29SAndy Yan 	u32 tmp;
28920b09c29SAndy Yan 	mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2);
29020b09c29SAndy Yan 	tmp = mvs_read_port_vsr_data(mvi, phy_id);
29120b09c29SAndy Yan 	mvs_write_port_vsr_data(mvi, phy_id, tmp | 0x00800000);
29220b09c29SAndy Yan }
29320b09c29SAndy Yan 
mvs_94xx_phy_enable(struct mvs_info * mvi,u32 phy_id)29420b09c29SAndy Yan static void mvs_94xx_phy_enable(struct mvs_info *mvi, u32 phy_id)
29520b09c29SAndy Yan {
296f1f82a91SXiangliang Yu 	u32 tmp;
297f1f82a91SXiangliang Yu 	u8 revision = 0;
298f1f82a91SXiangliang Yu 
299f1f82a91SXiangliang Yu 	revision = mvi->pdev->revision;
300f1f82a91SXiangliang Yu 	if (revision == VANIR_A0_REV) {
301f1f82a91SXiangliang Yu 		mvs_write_port_vsr_addr(mvi, phy_id, CMD_HOST_RD_DATA);
30220b09c29SAndy Yan 		mvs_write_port_vsr_data(mvi, phy_id, 0x8300ffc1);
303f1f82a91SXiangliang Yu 	}
304f1f82a91SXiangliang Yu 	if (revision == VANIR_B0_REV) {
305f1f82a91SXiangliang Yu 		mvs_write_port_vsr_addr(mvi, phy_id, CMD_APP_MEM_CTL);
306f1f82a91SXiangliang Yu 		mvs_write_port_vsr_data(mvi, phy_id, 0x08001006);
307f1f82a91SXiangliang Yu 		mvs_write_port_vsr_addr(mvi, phy_id, CMD_HOST_RD_DATA);
308f1f82a91SXiangliang Yu 		mvs_write_port_vsr_data(mvi, phy_id, 0x0000705f);
309f1f82a91SXiangliang Yu 	}
310f1f82a91SXiangliang Yu 
31120b09c29SAndy Yan 	mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2);
312f1f82a91SXiangliang Yu 	tmp = mvs_read_port_vsr_data(mvi, phy_id);
313f1f82a91SXiangliang Yu 	tmp |= bit(0);
314f1f82a91SXiangliang Yu 	mvs_write_port_vsr_data(mvi, phy_id, tmp & 0xfd7fffff);
31520b09c29SAndy Yan }
31620b09c29SAndy Yan 
mvs_94xx_sgpio_init(struct mvs_info * mvi)317c56f5f1dSWilfried Weissmann static void mvs_94xx_sgpio_init(struct mvs_info *mvi)
318c56f5f1dSWilfried Weissmann {
319c56f5f1dSWilfried Weissmann 	void __iomem *regs = mvi->regs_ex - 0x10200;
320c56f5f1dSWilfried Weissmann 	u32 tmp;
321c56f5f1dSWilfried Weissmann 
322c56f5f1dSWilfried Weissmann 	tmp = mr32(MVS_HST_CHIP_CONFIG);
323c56f5f1dSWilfried Weissmann 	tmp |= 0x100;
324c56f5f1dSWilfried Weissmann 	mw32(MVS_HST_CHIP_CONFIG, tmp);
325c56f5f1dSWilfried Weissmann 
326c56f5f1dSWilfried Weissmann 	mw32(MVS_SGPIO_CTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
327c56f5f1dSWilfried Weissmann 		MVS_SGPIO_CTRL_SDOUT_AUTO << MVS_SGPIO_CTRL_SDOUT_SHIFT);
328c56f5f1dSWilfried Weissmann 
329c56f5f1dSWilfried Weissmann 	mw32(MVS_SGPIO_CFG1 + MVS_SGPIO_HOST_OFFSET * mvi->id,
330c56f5f1dSWilfried Weissmann 		8 << MVS_SGPIO_CFG1_LOWA_SHIFT |
331c56f5f1dSWilfried Weissmann 		8 << MVS_SGPIO_CFG1_HIA_SHIFT |
332c56f5f1dSWilfried Weissmann 		4 << MVS_SGPIO_CFG1_LOWB_SHIFT |
333c56f5f1dSWilfried Weissmann 		4 << MVS_SGPIO_CFG1_HIB_SHIFT |
334c56f5f1dSWilfried Weissmann 		2 << MVS_SGPIO_CFG1_MAXACTON_SHIFT |
335c56f5f1dSWilfried Weissmann 		1 << MVS_SGPIO_CFG1_FORCEACTOFF_SHIFT
336c56f5f1dSWilfried Weissmann 	);
337c56f5f1dSWilfried Weissmann 
338c56f5f1dSWilfried Weissmann 	mw32(MVS_SGPIO_CFG2 + MVS_SGPIO_HOST_OFFSET * mvi->id,
339c56f5f1dSWilfried Weissmann 		(300000 / 100) << MVS_SGPIO_CFG2_CLK_SHIFT | /* 100kHz clock */
340c56f5f1dSWilfried Weissmann 		66 << MVS_SGPIO_CFG2_BLINK_SHIFT /* (66 * 0,121 Hz?)*/
341c56f5f1dSWilfried Weissmann 	);
342c56f5f1dSWilfried Weissmann 
343c56f5f1dSWilfried Weissmann 	mw32(MVS_SGPIO_CFG0 + MVS_SGPIO_HOST_OFFSET * mvi->id,
344c56f5f1dSWilfried Weissmann 		MVS_SGPIO_CFG0_ENABLE |
345c56f5f1dSWilfried Weissmann 		MVS_SGPIO_CFG0_BLINKA |
346c56f5f1dSWilfried Weissmann 		MVS_SGPIO_CFG0_BLINKB |
347c56f5f1dSWilfried Weissmann 		/* 3*4 data bits / PDU */
348c56f5f1dSWilfried Weissmann 		(12 - 1) << MVS_SGPIO_CFG0_AUT_BITLEN_SHIFT
349c56f5f1dSWilfried Weissmann 	);
350c56f5f1dSWilfried Weissmann 
351c56f5f1dSWilfried Weissmann 	mw32(MVS_SGPIO_DCTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
352c56f5f1dSWilfried Weissmann 		DEFAULT_SGPIO_BITS);
353c56f5f1dSWilfried Weissmann 
354c56f5f1dSWilfried Weissmann 	mw32(MVS_SGPIO_DSRC + MVS_SGPIO_HOST_OFFSET * mvi->id,
355c56f5f1dSWilfried Weissmann 		((mvi->id * 4) + 3) << (8 * 3) |
356c56f5f1dSWilfried Weissmann 		((mvi->id * 4) + 2) << (8 * 2) |
357c56f5f1dSWilfried Weissmann 		((mvi->id * 4) + 1) << (8 * 1) |
358c56f5f1dSWilfried Weissmann 		((mvi->id * 4) + 0) << (8 * 0));
359c56f5f1dSWilfried Weissmann 
360c56f5f1dSWilfried Weissmann }
361c56f5f1dSWilfried Weissmann 
mvs_94xx_init(struct mvs_info * mvi)3626f039790SGreg Kroah-Hartman static int mvs_94xx_init(struct mvs_info *mvi)
36320b09c29SAndy Yan {
36420b09c29SAndy Yan 	void __iomem *regs = mvi->regs;
36520b09c29SAndy Yan 	int i;
36620b09c29SAndy Yan 	u32 tmp, cctl;
367f1f82a91SXiangliang Yu 	u8 revision;
36820b09c29SAndy Yan 
369f1f82a91SXiangliang Yu 	revision = mvi->pdev->revision;
37020b09c29SAndy Yan 	mvs_show_pcie_usage(mvi);
37120b09c29SAndy Yan 	if (mvi->flags & MVF_FLAG_SOC) {
37220b09c29SAndy Yan 		tmp = mr32(MVS_PHY_CTL);
37320b09c29SAndy Yan 		tmp &= ~PCTL_PWR_OFF;
37420b09c29SAndy Yan 		tmp |= PCTL_PHY_DSBL;
37520b09c29SAndy Yan 		mw32(MVS_PHY_CTL, tmp);
37620b09c29SAndy Yan 	}
37720b09c29SAndy Yan 
37820b09c29SAndy Yan 	/* Init Chip */
37920b09c29SAndy Yan 	/* make sure RST is set; HBA_RST /should/ have done that for us */
38020b09c29SAndy Yan 	cctl = mr32(MVS_CTL) & 0xFFFF;
38120b09c29SAndy Yan 	if (cctl & CCTL_RST)
38220b09c29SAndy Yan 		cctl &= ~CCTL_RST;
38320b09c29SAndy Yan 	else
38420b09c29SAndy Yan 		mw32_f(MVS_CTL, cctl | CCTL_RST);
38520b09c29SAndy Yan 
38620b09c29SAndy Yan 	if (mvi->flags & MVF_FLAG_SOC) {
38720b09c29SAndy Yan 		tmp = mr32(MVS_PHY_CTL);
38820b09c29SAndy Yan 		tmp &= ~PCTL_PWR_OFF;
38920b09c29SAndy Yan 		tmp |= PCTL_COM_ON;
39020b09c29SAndy Yan 		tmp &= ~PCTL_PHY_DSBL;
39120b09c29SAndy Yan 		tmp |= PCTL_LINK_RST;
39220b09c29SAndy Yan 		mw32(MVS_PHY_CTL, tmp);
39320b09c29SAndy Yan 		msleep(100);
39420b09c29SAndy Yan 		tmp &= ~PCTL_LINK_RST;
39520b09c29SAndy Yan 		mw32(MVS_PHY_CTL, tmp);
39620b09c29SAndy Yan 		msleep(100);
39720b09c29SAndy Yan 	}
39820b09c29SAndy Yan 
399f1f82a91SXiangliang Yu 	/* disable Multiplexing, enable phy implemented */
400f1f82a91SXiangliang Yu 	mw32(MVS_PORTS_IMP, 0xFF);
401f1f82a91SXiangliang Yu 
402f1f82a91SXiangliang Yu 	if (revision == VANIR_A0_REV) {
403f1f82a91SXiangliang Yu 		mw32(MVS_PA_VSR_ADDR, CMD_CMWK_OOB_DET);
404f1f82a91SXiangliang Yu 		mw32(MVS_PA_VSR_PORT, 0x00018080);
405f1f82a91SXiangliang Yu 	}
406f1f82a91SXiangliang Yu 	mw32(MVS_PA_VSR_ADDR, VSR_PHY_MODE2);
407f1f82a91SXiangliang Yu 	if (revision == VANIR_A0_REV || revision == VANIR_B0_REV)
408f1f82a91SXiangliang Yu 		/* set 6G/3G/1.5G, multiplexing, without SSC */
409f1f82a91SXiangliang Yu 		mw32(MVS_PA_VSR_PORT, 0x0084d4fe);
410f1f82a91SXiangliang Yu 	else
411f1f82a91SXiangliang Yu 		/* set 6G/3G/1.5G, multiplexing, with and without SSC */
412f1f82a91SXiangliang Yu 		mw32(MVS_PA_VSR_PORT, 0x0084fffe);
413f1f82a91SXiangliang Yu 
414f1f82a91SXiangliang Yu 	if (revision == VANIR_B0_REV) {
415f1f82a91SXiangliang Yu 		mw32(MVS_PA_VSR_ADDR, CMD_APP_MEM_CTL);
416f1f82a91SXiangliang Yu 		mw32(MVS_PA_VSR_PORT, 0x08001006);
417f1f82a91SXiangliang Yu 		mw32(MVS_PA_VSR_ADDR, CMD_HOST_RD_DATA);
418f1f82a91SXiangliang Yu 		mw32(MVS_PA_VSR_PORT, 0x0000705f);
419f1f82a91SXiangliang Yu 	}
420f1f82a91SXiangliang Yu 
42120b09c29SAndy Yan 	/* reset control */
42220b09c29SAndy Yan 	mw32(MVS_PCS, 0);		/* MVS_PCS */
42320b09c29SAndy Yan 	mw32(MVS_STP_REG_SET_0, 0);
42420b09c29SAndy Yan 	mw32(MVS_STP_REG_SET_1, 0);
42520b09c29SAndy Yan 
42620b09c29SAndy Yan 	/* init phys */
42720b09c29SAndy Yan 	mvs_phy_hacks(mvi);
42820b09c29SAndy Yan 
42907f098e6SXiangliang Yu 	/* disable non data frame retry */
43007f098e6SXiangliang Yu 	tmp = mvs_cr32(mvi, CMD_SAS_CTL1);
43107f098e6SXiangliang Yu 	if ((revision == VANIR_A0_REV) ||
43207f098e6SXiangliang Yu 		(revision == VANIR_B0_REV) ||
43307f098e6SXiangliang Yu 		(revision == VANIR_C0_REV)) {
43407f098e6SXiangliang Yu 		tmp &= ~0xffff;
43507f098e6SXiangliang Yu 		tmp |= 0x007f;
43607f098e6SXiangliang Yu 		mvs_cw32(mvi, CMD_SAS_CTL1, tmp);
43707f098e6SXiangliang Yu 	}
43807f098e6SXiangliang Yu 
43920b09c29SAndy Yan 	/* set LED blink when IO*/
440a4632aaeSXiangliang Yu 	mw32(MVS_PA_VSR_ADDR, VSR_PHY_ACT_LED);
44120b09c29SAndy Yan 	tmp = mr32(MVS_PA_VSR_PORT);
44220b09c29SAndy Yan 	tmp &= 0xFFFF00FF;
44320b09c29SAndy Yan 	tmp |= 0x00003300;
44420b09c29SAndy Yan 	mw32(MVS_PA_VSR_PORT, tmp);
44520b09c29SAndy Yan 
44620b09c29SAndy Yan 	mw32(MVS_CMD_LIST_LO, mvi->slot_dma);
44720b09c29SAndy Yan 	mw32(MVS_CMD_LIST_HI, (mvi->slot_dma >> 16) >> 16);
44820b09c29SAndy Yan 
44920b09c29SAndy Yan 	mw32(MVS_RX_FIS_LO, mvi->rx_fis_dma);
45020b09c29SAndy Yan 	mw32(MVS_RX_FIS_HI, (mvi->rx_fis_dma >> 16) >> 16);
45120b09c29SAndy Yan 
45220b09c29SAndy Yan 	mw32(MVS_TX_CFG, MVS_CHIP_SLOT_SZ);
45320b09c29SAndy Yan 	mw32(MVS_TX_LO, mvi->tx_dma);
45420b09c29SAndy Yan 	mw32(MVS_TX_HI, (mvi->tx_dma >> 16) >> 16);
45520b09c29SAndy Yan 
45620b09c29SAndy Yan 	mw32(MVS_RX_CFG, MVS_RX_RING_SZ);
45720b09c29SAndy Yan 	mw32(MVS_RX_LO, mvi->rx_dma);
45820b09c29SAndy Yan 	mw32(MVS_RX_HI, (mvi->rx_dma >> 16) >> 16);
45920b09c29SAndy Yan 
46020b09c29SAndy Yan 	for (i = 0; i < mvi->chip->n_phy; i++) {
46120b09c29SAndy Yan 		mvs_94xx_phy_disable(mvi, i);
46220b09c29SAndy Yan 		/* set phy local SAS address */
46320b09c29SAndy Yan 		mvs_set_sas_addr(mvi, i, CONFIG_ID_FRAME3, CONFIG_ID_FRAME4,
46484fbd0ceSXiangliang Yu 						cpu_to_le64(mvi->phy[i].dev_sas_addr));
46520b09c29SAndy Yan 
46620b09c29SAndy Yan 		mvs_94xx_enable_xmt(mvi, i);
467f1f82a91SXiangliang Yu 		mvs_94xx_config_reg_from_hba(mvi, i);
46820b09c29SAndy Yan 		mvs_94xx_phy_enable(mvi, i);
46920b09c29SAndy Yan 
470a4632aaeSXiangliang Yu 		mvs_94xx_phy_reset(mvi, i, PHY_RST_HARD);
47120b09c29SAndy Yan 		msleep(500);
47220b09c29SAndy Yan 		mvs_94xx_detect_porttype(mvi, i);
47320b09c29SAndy Yan 	}
47420b09c29SAndy Yan 
47520b09c29SAndy Yan 	if (mvi->flags & MVF_FLAG_SOC) {
47620b09c29SAndy Yan 		/* set select registers */
47720b09c29SAndy Yan 		writel(0x0E008000, regs + 0x000);
47820b09c29SAndy Yan 		writel(0x59000008, regs + 0x004);
47920b09c29SAndy Yan 		writel(0x20, regs + 0x008);
48020b09c29SAndy Yan 		writel(0x20, regs + 0x00c);
48120b09c29SAndy Yan 		writel(0x20, regs + 0x010);
48220b09c29SAndy Yan 		writel(0x20, regs + 0x014);
48320b09c29SAndy Yan 		writel(0x20, regs + 0x018);
48420b09c29SAndy Yan 		writel(0x20, regs + 0x01c);
48520b09c29SAndy Yan 	}
48620b09c29SAndy Yan 	for (i = 0; i < mvi->chip->n_phy; i++) {
48720b09c29SAndy Yan 		/* clear phy int status */
48820b09c29SAndy Yan 		tmp = mvs_read_port_irq_stat(mvi, i);
48920b09c29SAndy Yan 		tmp &= ~PHYEV_SIG_FIS;
49020b09c29SAndy Yan 		mvs_write_port_irq_stat(mvi, i, tmp);
49120b09c29SAndy Yan 
49220b09c29SAndy Yan 		/* set phy int mask */
49320b09c29SAndy Yan 		tmp = PHYEV_RDY_CH | PHYEV_BROAD_CH |
49420b09c29SAndy Yan 			PHYEV_ID_DONE  | PHYEV_DCDR_ERR | PHYEV_CRC_ERR ;
49520b09c29SAndy Yan 		mvs_write_port_irq_mask(mvi, i, tmp);
49620b09c29SAndy Yan 
49720b09c29SAndy Yan 		msleep(100);
49820b09c29SAndy Yan 		mvs_update_phyinfo(mvi, i, 1);
49920b09c29SAndy Yan 	}
50020b09c29SAndy Yan 
50120b09c29SAndy Yan 	/* little endian for open address and command table, etc. */
50220b09c29SAndy Yan 	cctl = mr32(MVS_CTL);
50320b09c29SAndy Yan 	cctl |= CCTL_ENDIAN_CMD;
50420b09c29SAndy Yan 	cctl &= ~CCTL_ENDIAN_OPEN;
50520b09c29SAndy Yan 	cctl |= CCTL_ENDIAN_RSP;
50620b09c29SAndy Yan 	mw32_f(MVS_CTL, cctl);
50720b09c29SAndy Yan 
50820b09c29SAndy Yan 	/* reset CMD queue */
50920b09c29SAndy Yan 	tmp = mr32(MVS_PCS);
51020b09c29SAndy Yan 	tmp |= PCS_CMD_RST;
51184fbd0ceSXiangliang Yu 	tmp &= ~PCS_SELF_CLEAR;
51220b09c29SAndy Yan 	mw32(MVS_PCS, tmp);
513e144f7efSXiangliang Yu 	/*
514e144f7efSXiangliang Yu 	 * the max count is 0x1ff, while our max slot is 0x200,
51520b09c29SAndy Yan 	 * it will make count 0.
51620b09c29SAndy Yan 	 */
51720b09c29SAndy Yan 	tmp = 0;
51884fbd0ceSXiangliang Yu 	if (MVS_CHIP_SLOT_SZ > 0x1ff)
51984fbd0ceSXiangliang Yu 		mw32(MVS_INT_COAL, 0x1ff | COAL_EN);
52084fbd0ceSXiangliang Yu 	else
52184fbd0ceSXiangliang Yu 		mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ | COAL_EN);
52220b09c29SAndy Yan 
523e144f7efSXiangliang Yu 	/* default interrupt coalescing time is 128us */
52483c7b61cSXiangliang Yu 	tmp = 0x10000 | interrupt_coalescing;
52520b09c29SAndy Yan 	mw32(MVS_INT_COAL_TMOUT, tmp);
52620b09c29SAndy Yan 
52720b09c29SAndy Yan 	/* ladies and gentlemen, start your engines */
52820b09c29SAndy Yan 	mw32(MVS_TX_CFG, 0);
52920b09c29SAndy Yan 	mw32(MVS_TX_CFG, MVS_CHIP_SLOT_SZ | TX_EN);
53020b09c29SAndy Yan 	mw32(MVS_RX_CFG, MVS_RX_RING_SZ | RX_EN);
53120b09c29SAndy Yan 	/* enable CMD/CMPL_Q/RESP mode */
53220b09c29SAndy Yan 	mw32(MVS_PCS, PCS_SATA_RETRY_2 | PCS_FIS_RX_EN |
53320b09c29SAndy Yan 		PCS_CMD_EN | PCS_CMD_STOP_ERR);
53420b09c29SAndy Yan 
53520b09c29SAndy Yan 	/* enable completion queue interrupt */
53620b09c29SAndy Yan 	tmp = (CINT_PORT_MASK | CINT_DONE | CINT_MEM | CINT_SRS | CINT_CI_STOP |
537534ff101SXiangliang Yu 		CINT_DMA_PCIE | CINT_NON_SPEC_NCQ_ERROR);
53820b09c29SAndy Yan 	tmp |= CINT_PHY_MASK;
53920b09c29SAndy Yan 	mw32(MVS_INT_MASK, tmp);
54020b09c29SAndy Yan 
54140d3921fSXiangliang Yu 	tmp = mvs_cr32(mvi, CMD_LINK_TIMER);
54240d3921fSXiangliang Yu 	tmp |= 0xFFFF0000;
54340d3921fSXiangliang Yu 	mvs_cw32(mvi, CMD_LINK_TIMER, tmp);
54440d3921fSXiangliang Yu 
5453a4b7efeSXiangliang Yu 	/* tune STP performance */
5463a4b7efeSXiangliang Yu 	tmp = 0x003F003F;
5473a4b7efeSXiangliang Yu 	mvs_cw32(mvi, CMD_PL_TIMER, tmp);
5483a4b7efeSXiangliang Yu 
5493a4b7efeSXiangliang Yu 	/* This can improve expander large block size seq write performance */
5503a4b7efeSXiangliang Yu 	tmp = mvs_cr32(mvi, CMD_PORT_LAYER_TIMER1);
5513a4b7efeSXiangliang Yu 	tmp |= 0xFFFF007F;
5523a4b7efeSXiangliang Yu 	mvs_cw32(mvi, CMD_PORT_LAYER_TIMER1, tmp);
5533a4b7efeSXiangliang Yu 
554aa117dd1SXiangliang Yu 	/* change the connection open-close behavior (bit 9)
555aa117dd1SXiangliang Yu 	 * set bit8 to 1 for performance tuning */
556aa117dd1SXiangliang Yu 	tmp = mvs_cr32(mvi, CMD_SL_MODE0);
557aa117dd1SXiangliang Yu 	tmp |= 0x00000300;
558aa117dd1SXiangliang Yu 	/* set bit0 to 0 to enable retry for no_dest reject case */
559aa117dd1SXiangliang Yu 	tmp &= 0xFFFFFFFE;
560aa117dd1SXiangliang Yu 	mvs_cw32(mvi, CMD_SL_MODE0, tmp);
561aa117dd1SXiangliang Yu 
56220b09c29SAndy Yan 	/* Enable SRS interrupt */
56320b09c29SAndy Yan 	mw32(MVS_INT_MASK_SRS_0, 0xFFFF);
56420b09c29SAndy Yan 
565c56f5f1dSWilfried Weissmann 	mvs_94xx_sgpio_init(mvi);
566c56f5f1dSWilfried Weissmann 
56720b09c29SAndy Yan 	return 0;
56820b09c29SAndy Yan }
56920b09c29SAndy Yan 
mvs_94xx_ioremap(struct mvs_info * mvi)57020b09c29SAndy Yan static int mvs_94xx_ioremap(struct mvs_info *mvi)
57120b09c29SAndy Yan {
57220b09c29SAndy Yan 	if (!mvs_ioremap(mvi, 2, -1)) {
57320b09c29SAndy Yan 		mvi->regs_ex = mvi->regs + 0x10200;
57420b09c29SAndy Yan 		mvi->regs += 0x20000;
57520b09c29SAndy Yan 		if (mvi->id == 1)
57620b09c29SAndy Yan 			mvi->regs += 0x4000;
57720b09c29SAndy Yan 		return 0;
57820b09c29SAndy Yan 	}
57920b09c29SAndy Yan 	return -1;
58020b09c29SAndy Yan }
58120b09c29SAndy Yan 
mvs_94xx_iounmap(struct mvs_info * mvi)58220b09c29SAndy Yan static void mvs_94xx_iounmap(struct mvs_info *mvi)
58320b09c29SAndy Yan {
58420b09c29SAndy Yan 	if (mvi->regs) {
58520b09c29SAndy Yan 		mvi->regs -= 0x20000;
58620b09c29SAndy Yan 		if (mvi->id == 1)
58720b09c29SAndy Yan 			mvi->regs -= 0x4000;
58820b09c29SAndy Yan 		mvs_iounmap(mvi->regs);
58920b09c29SAndy Yan 	}
59020b09c29SAndy Yan }
59120b09c29SAndy Yan 
mvs_94xx_interrupt_enable(struct mvs_info * mvi)59220b09c29SAndy Yan static void mvs_94xx_interrupt_enable(struct mvs_info *mvi)
59320b09c29SAndy Yan {
59420b09c29SAndy Yan 	void __iomem *regs = mvi->regs_ex;
59520b09c29SAndy Yan 	u32 tmp;
59620b09c29SAndy Yan 
59720b09c29SAndy Yan 	tmp = mr32(MVS_GBL_CTL);
5988902b107SChen Gang 	tmp |= (MVS_IRQ_SAS_A | MVS_IRQ_SAS_B);
59920b09c29SAndy Yan 	mw32(MVS_GBL_INT_STAT, tmp);
60020b09c29SAndy Yan 	writel(tmp, regs + 0x0C);
60120b09c29SAndy Yan 	writel(tmp, regs + 0x10);
60220b09c29SAndy Yan 	writel(tmp, regs + 0x14);
60320b09c29SAndy Yan 	writel(tmp, regs + 0x18);
60420b09c29SAndy Yan 	mw32(MVS_GBL_CTL, tmp);
60520b09c29SAndy Yan }
60620b09c29SAndy Yan 
mvs_94xx_interrupt_disable(struct mvs_info * mvi)60720b09c29SAndy Yan static void mvs_94xx_interrupt_disable(struct mvs_info *mvi)
60820b09c29SAndy Yan {
60920b09c29SAndy Yan 	void __iomem *regs = mvi->regs_ex;
61020b09c29SAndy Yan 	u32 tmp;
61120b09c29SAndy Yan 
61220b09c29SAndy Yan 	tmp = mr32(MVS_GBL_CTL);
61320b09c29SAndy Yan 
6148902b107SChen Gang 	tmp &= ~(MVS_IRQ_SAS_A | MVS_IRQ_SAS_B);
61520b09c29SAndy Yan 	mw32(MVS_GBL_INT_STAT, tmp);
61620b09c29SAndy Yan 	writel(tmp, regs + 0x0C);
61720b09c29SAndy Yan 	writel(tmp, regs + 0x10);
61820b09c29SAndy Yan 	writel(tmp, regs + 0x14);
61920b09c29SAndy Yan 	writel(tmp, regs + 0x18);
62020b09c29SAndy Yan 	mw32(MVS_GBL_CTL, tmp);
62120b09c29SAndy Yan }
62220b09c29SAndy Yan 
mvs_94xx_isr_status(struct mvs_info * mvi,int irq)62320b09c29SAndy Yan static u32 mvs_94xx_isr_status(struct mvs_info *mvi, int irq)
62420b09c29SAndy Yan {
62520b09c29SAndy Yan 	void __iomem *regs = mvi->regs_ex;
62620b09c29SAndy Yan 	u32 stat = 0;
62720b09c29SAndy Yan 	if (!(mvi->flags & MVF_FLAG_SOC)) {
62820b09c29SAndy Yan 		stat = mr32(MVS_GBL_INT_STAT);
62920b09c29SAndy Yan 
6308902b107SChen Gang 		if (!(stat & (MVS_IRQ_SAS_A | MVS_IRQ_SAS_B)))
63120b09c29SAndy Yan 			return 0;
63220b09c29SAndy Yan 	}
63320b09c29SAndy Yan 	return stat;
63420b09c29SAndy Yan }
63520b09c29SAndy Yan 
mvs_94xx_isr(struct mvs_info * mvi,int irq,u32 stat)63620b09c29SAndy Yan static irqreturn_t mvs_94xx_isr(struct mvs_info *mvi, int irq, u32 stat)
63720b09c29SAndy Yan {
63820b09c29SAndy Yan 	void __iomem *regs = mvi->regs;
63920b09c29SAndy Yan 
6408902b107SChen Gang 	if (((stat & MVS_IRQ_SAS_A) && mvi->id == 0) ||
6418902b107SChen Gang 			((stat & MVS_IRQ_SAS_B) && mvi->id == 1)) {
64220b09c29SAndy Yan 		mw32_f(MVS_INT_STAT, CINT_DONE);
6436f8ac161SXiangliang Yu 
64420b09c29SAndy Yan 		spin_lock(&mvi->lock);
64520b09c29SAndy Yan 		mvs_int_full(mvi);
64620b09c29SAndy Yan 		spin_unlock(&mvi->lock);
64720b09c29SAndy Yan 	}
64820b09c29SAndy Yan 	return IRQ_HANDLED;
64920b09c29SAndy Yan }
65020b09c29SAndy Yan 
mvs_94xx_command_active(struct mvs_info * mvi,u32 slot_idx)65120b09c29SAndy Yan static void mvs_94xx_command_active(struct mvs_info *mvi, u32 slot_idx)
65220b09c29SAndy Yan {
65320b09c29SAndy Yan 	u32 tmp;
654a4632aaeSXiangliang Yu 	tmp = mvs_cr32(mvi, MVS_COMMAND_ACTIVE+(slot_idx >> 3));
655af15769fSArnd Bergmann 	if (tmp & 1 << (slot_idx % 32)) {
656a4632aaeSXiangliang Yu 		mv_printk("command active %08X,  slot [%x].\n", tmp, slot_idx);
657a4632aaeSXiangliang Yu 		mvs_cw32(mvi, MVS_COMMAND_ACTIVE + (slot_idx >> 3),
658a4632aaeSXiangliang Yu 			1 << (slot_idx % 32));
65920b09c29SAndy Yan 		do {
660a4632aaeSXiangliang Yu 			tmp = mvs_cr32(mvi,
661a4632aaeSXiangliang Yu 				MVS_COMMAND_ACTIVE + (slot_idx >> 3));
66220b09c29SAndy Yan 		} while (tmp & 1 << (slot_idx % 32));
66320b09c29SAndy Yan 	}
664a4632aaeSXiangliang Yu }
665a4632aaeSXiangliang Yu 
66614bf41dcSBaoyou Xie static void
mvs_94xx_clear_srs_irq(struct mvs_info * mvi,u8 reg_set,u8 clear_all)66714bf41dcSBaoyou Xie mvs_94xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all)
668a4632aaeSXiangliang Yu {
669a4632aaeSXiangliang Yu 	void __iomem *regs = mvi->regs;
670a4632aaeSXiangliang Yu 	u32 tmp;
671a4632aaeSXiangliang Yu 
672a4632aaeSXiangliang Yu 	if (clear_all) {
673a4632aaeSXiangliang Yu 		tmp = mr32(MVS_INT_STAT_SRS_0);
674a4632aaeSXiangliang Yu 		if (tmp) {
675a4632aaeSXiangliang Yu 			mv_dprintk("check SRS 0 %08X.\n", tmp);
676a4632aaeSXiangliang Yu 			mw32(MVS_INT_STAT_SRS_0, tmp);
677a4632aaeSXiangliang Yu 		}
678a4632aaeSXiangliang Yu 		tmp = mr32(MVS_INT_STAT_SRS_1);
679a4632aaeSXiangliang Yu 		if (tmp) {
680a4632aaeSXiangliang Yu 			mv_dprintk("check SRS 1 %08X.\n", tmp);
681a4632aaeSXiangliang Yu 			mw32(MVS_INT_STAT_SRS_1, tmp);
682a4632aaeSXiangliang Yu 		}
683a4632aaeSXiangliang Yu 	} else {
684a4632aaeSXiangliang Yu 		if (reg_set > 31)
685a4632aaeSXiangliang Yu 			tmp = mr32(MVS_INT_STAT_SRS_1);
686a4632aaeSXiangliang Yu 		else
687a4632aaeSXiangliang Yu 			tmp = mr32(MVS_INT_STAT_SRS_0);
688a4632aaeSXiangliang Yu 
689a4632aaeSXiangliang Yu 		if (tmp & (1 << (reg_set % 32))) {
690a4632aaeSXiangliang Yu 			mv_dprintk("register set 0x%x was stopped.\n", reg_set);
691a4632aaeSXiangliang Yu 			if (reg_set > 31)
692a4632aaeSXiangliang Yu 				mw32(MVS_INT_STAT_SRS_1, 1 << (reg_set % 32));
693a4632aaeSXiangliang Yu 			else
694a4632aaeSXiangliang Yu 				mw32(MVS_INT_STAT_SRS_0, 1 << (reg_set % 32));
695a4632aaeSXiangliang Yu 		}
696a4632aaeSXiangliang Yu 	}
697a4632aaeSXiangliang Yu }
69820b09c29SAndy Yan 
mvs_94xx_issue_stop(struct mvs_info * mvi,enum mvs_port_type type,u32 tfs)69920b09c29SAndy Yan static void mvs_94xx_issue_stop(struct mvs_info *mvi, enum mvs_port_type type,
70020b09c29SAndy Yan 				u32 tfs)
70120b09c29SAndy Yan {
70220b09c29SAndy Yan 	void __iomem *regs = mvi->regs;
70320b09c29SAndy Yan 	u32 tmp;
704a4632aaeSXiangliang Yu 	mvs_94xx_clear_srs_irq(mvi, 0, 1);
70520b09c29SAndy Yan 
706a4632aaeSXiangliang Yu 	tmp = mr32(MVS_INT_STAT);
707a4632aaeSXiangliang Yu 	mw32(MVS_INT_STAT, tmp | CINT_CI_STOP);
70820b09c29SAndy Yan 	tmp = mr32(MVS_PCS) | 0xFF00;
70920b09c29SAndy Yan 	mw32(MVS_PCS, tmp);
71020b09c29SAndy Yan }
71120b09c29SAndy Yan 
mvs_94xx_non_spec_ncq_error(struct mvs_info * mvi)712534ff101SXiangliang Yu static void mvs_94xx_non_spec_ncq_error(struct mvs_info *mvi)
713534ff101SXiangliang Yu {
714534ff101SXiangliang Yu 	void __iomem *regs = mvi->regs;
715534ff101SXiangliang Yu 	u32 err_0, err_1;
716534ff101SXiangliang Yu 	u8 i;
717534ff101SXiangliang Yu 	struct mvs_device *device;
718534ff101SXiangliang Yu 
719534ff101SXiangliang Yu 	err_0 = mr32(MVS_NON_NCQ_ERR_0);
720534ff101SXiangliang Yu 	err_1 = mr32(MVS_NON_NCQ_ERR_1);
721534ff101SXiangliang Yu 
722534ff101SXiangliang Yu 	mv_dprintk("non specific ncq error err_0:%x,err_1:%x.\n",
723534ff101SXiangliang Yu 			err_0, err_1);
724534ff101SXiangliang Yu 	for (i = 0; i < 32; i++) {
725534ff101SXiangliang Yu 		if (err_0 & bit(i)) {
726534ff101SXiangliang Yu 			device = mvs_find_dev_by_reg_set(mvi, i);
727534ff101SXiangliang Yu 			if (device)
728534ff101SXiangliang Yu 				mvs_release_task(mvi, device->sas_device);
729534ff101SXiangliang Yu 		}
730534ff101SXiangliang Yu 		if (err_1 & bit(i)) {
731534ff101SXiangliang Yu 			device = mvs_find_dev_by_reg_set(mvi, i+32);
732534ff101SXiangliang Yu 			if (device)
733534ff101SXiangliang Yu 				mvs_release_task(mvi, device->sas_device);
734534ff101SXiangliang Yu 		}
735534ff101SXiangliang Yu 	}
736534ff101SXiangliang Yu 
737534ff101SXiangliang Yu 	mw32(MVS_NON_NCQ_ERR_0, err_0);
738534ff101SXiangliang Yu 	mw32(MVS_NON_NCQ_ERR_1, err_1);
739534ff101SXiangliang Yu }
740534ff101SXiangliang Yu 
mvs_94xx_free_reg_set(struct mvs_info * mvi,u8 * tfs)74120b09c29SAndy Yan static void mvs_94xx_free_reg_set(struct mvs_info *mvi, u8 *tfs)
74220b09c29SAndy Yan {
74320b09c29SAndy Yan 	void __iomem *regs = mvi->regs;
74420b09c29SAndy Yan 	u8 reg_set = *tfs;
74520b09c29SAndy Yan 
74620b09c29SAndy Yan 	if (*tfs == MVS_ID_NOT_MAPPED)
74720b09c29SAndy Yan 		return;
74820b09c29SAndy Yan 
74920b09c29SAndy Yan 	mvi->sata_reg_set &= ~bit(reg_set);
75084fbd0ceSXiangliang Yu 	if (reg_set < 32)
75120b09c29SAndy Yan 		w_reg_set_enable(reg_set, (u32)mvi->sata_reg_set);
75284fbd0ceSXiangliang Yu 	else
75384fbd0ceSXiangliang Yu 		w_reg_set_enable(reg_set, (u32)(mvi->sata_reg_set >> 32));
75420b09c29SAndy Yan 
75520b09c29SAndy Yan 	*tfs = MVS_ID_NOT_MAPPED;
75620b09c29SAndy Yan 
75720b09c29SAndy Yan 	return;
75820b09c29SAndy Yan }
75920b09c29SAndy Yan 
mvs_94xx_assign_reg_set(struct mvs_info * mvi,u8 * tfs)76020b09c29SAndy Yan static u8 mvs_94xx_assign_reg_set(struct mvs_info *mvi, u8 *tfs)
76120b09c29SAndy Yan {
76220b09c29SAndy Yan 	int i;
76320b09c29SAndy Yan 	void __iomem *regs = mvi->regs;
76420b09c29SAndy Yan 
76520b09c29SAndy Yan 	if (*tfs != MVS_ID_NOT_MAPPED)
76620b09c29SAndy Yan 		return 0;
76720b09c29SAndy Yan 
76820b09c29SAndy Yan 	i = mv_ffc64(mvi->sata_reg_set);
76984fbd0ceSXiangliang Yu 	if (i >= 32) {
77020b09c29SAndy Yan 		mvi->sata_reg_set |= bit(i);
77120b09c29SAndy Yan 		w_reg_set_enable(i, (u32)(mvi->sata_reg_set >> 32));
77220b09c29SAndy Yan 		*tfs = i;
77320b09c29SAndy Yan 		return 0;
77420b09c29SAndy Yan 	} else if (i >= 0) {
77520b09c29SAndy Yan 		mvi->sata_reg_set |= bit(i);
77620b09c29SAndy Yan 		w_reg_set_enable(i, (u32)mvi->sata_reg_set);
77720b09c29SAndy Yan 		*tfs = i;
77820b09c29SAndy Yan 		return 0;
77920b09c29SAndy Yan 	}
78020b09c29SAndy Yan 	return MVS_ID_NOT_MAPPED;
78120b09c29SAndy Yan }
78220b09c29SAndy Yan 
mvs_94xx_make_prd(struct scatterlist * scatter,int nr,void * prd)78320b09c29SAndy Yan static void mvs_94xx_make_prd(struct scatterlist *scatter, int nr, void *prd)
78420b09c29SAndy Yan {
78520b09c29SAndy Yan 	int i;
78620b09c29SAndy Yan 	struct scatterlist *sg;
78720b09c29SAndy Yan 	struct mvs_prd *buf_prd = prd;
78884fbd0ceSXiangliang Yu 	struct mvs_prd_imt im_len;
78984fbd0ceSXiangliang Yu 	*(u32 *)&im_len = 0;
79020b09c29SAndy Yan 	for_each_sg(scatter, sg, nr, i) {
79120b09c29SAndy Yan 		buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
79284fbd0ceSXiangliang Yu 		im_len.len = sg_dma_len(sg);
79384fbd0ceSXiangliang Yu 		buf_prd->im_len = cpu_to_le32(*(u32 *)&im_len);
79420b09c29SAndy Yan 		buf_prd++;
79520b09c29SAndy Yan 	}
79620b09c29SAndy Yan }
79720b09c29SAndy Yan 
mvs_94xx_oob_done(struct mvs_info * mvi,int i)79820b09c29SAndy Yan static int mvs_94xx_oob_done(struct mvs_info *mvi, int i)
79920b09c29SAndy Yan {
80020b09c29SAndy Yan 	u32 phy_st;
80120b09c29SAndy Yan 	phy_st = mvs_read_phy_ctl(mvi, i);
802e144f7efSXiangliang Yu 	if (phy_st & PHY_READY_MASK)
80320b09c29SAndy Yan 		return 1;
80420b09c29SAndy Yan 	return 0;
80520b09c29SAndy Yan }
80620b09c29SAndy Yan 
mvs_94xx_get_dev_identify_frame(struct mvs_info * mvi,int port_id,struct sas_identify_frame * id)80720b09c29SAndy Yan static void mvs_94xx_get_dev_identify_frame(struct mvs_info *mvi, int port_id,
80820b09c29SAndy Yan 					struct sas_identify_frame *id)
80920b09c29SAndy Yan {
81020b09c29SAndy Yan 	int i;
81120b09c29SAndy Yan 	u32 id_frame[7];
81220b09c29SAndy Yan 
81320b09c29SAndy Yan 	for (i = 0; i < 7; i++) {
81420b09c29SAndy Yan 		mvs_write_port_cfg_addr(mvi, port_id,
81520b09c29SAndy Yan 					CONFIG_ID_FRAME0 + i * 4);
81684fbd0ceSXiangliang Yu 		id_frame[i] = cpu_to_le32(mvs_read_port_cfg_data(mvi, port_id));
81720b09c29SAndy Yan 	}
81820b09c29SAndy Yan 	memcpy(id, id_frame, 28);
81920b09c29SAndy Yan }
82020b09c29SAndy Yan 
mvs_94xx_get_att_identify_frame(struct mvs_info * mvi,int port_id,struct sas_identify_frame * id)82120b09c29SAndy Yan static void mvs_94xx_get_att_identify_frame(struct mvs_info *mvi, int port_id,
82220b09c29SAndy Yan 					struct sas_identify_frame *id)
82320b09c29SAndy Yan {
82420b09c29SAndy Yan 	int i;
82520b09c29SAndy Yan 	u32 id_frame[7];
82620b09c29SAndy Yan 
82720b09c29SAndy Yan 	for (i = 0; i < 7; i++) {
82820b09c29SAndy Yan 		mvs_write_port_cfg_addr(mvi, port_id,
82920b09c29SAndy Yan 					CONFIG_ATT_ID_FRAME0 + i * 4);
83084fbd0ceSXiangliang Yu 		id_frame[i] = cpu_to_le32(mvs_read_port_cfg_data(mvi, port_id));
83120b09c29SAndy Yan 		mv_dprintk("94xx phy %d atta frame %d %x.\n",
83220b09c29SAndy Yan 			port_id + mvi->id * mvi->chip->n_phy, i, id_frame[i]);
83320b09c29SAndy Yan 	}
83420b09c29SAndy Yan 	memcpy(id, id_frame, 28);
83520b09c29SAndy Yan }
83620b09c29SAndy Yan 
mvs_94xx_make_dev_info(struct sas_identify_frame * id)83720b09c29SAndy Yan static u32 mvs_94xx_make_dev_info(struct sas_identify_frame *id)
83820b09c29SAndy Yan {
83920b09c29SAndy Yan 	u32 att_dev_info = 0;
84020b09c29SAndy Yan 
84120b09c29SAndy Yan 	att_dev_info |= id->dev_type;
84220b09c29SAndy Yan 	if (id->stp_iport)
84320b09c29SAndy Yan 		att_dev_info |= PORT_DEV_STP_INIT;
84420b09c29SAndy Yan 	if (id->smp_iport)
84520b09c29SAndy Yan 		att_dev_info |= PORT_DEV_SMP_INIT;
84620b09c29SAndy Yan 	if (id->ssp_iport)
84720b09c29SAndy Yan 		att_dev_info |= PORT_DEV_SSP_INIT;
84820b09c29SAndy Yan 	if (id->stp_tport)
84920b09c29SAndy Yan 		att_dev_info |= PORT_DEV_STP_TRGT;
85020b09c29SAndy Yan 	if (id->smp_tport)
85120b09c29SAndy Yan 		att_dev_info |= PORT_DEV_SMP_TRGT;
85220b09c29SAndy Yan 	if (id->ssp_tport)
85320b09c29SAndy Yan 		att_dev_info |= PORT_DEV_SSP_TRGT;
85420b09c29SAndy Yan 
85520b09c29SAndy Yan 	att_dev_info |= (u32)id->phy_id<<24;
85620b09c29SAndy Yan 	return att_dev_info;
85720b09c29SAndy Yan }
85820b09c29SAndy Yan 
mvs_94xx_make_att_info(struct sas_identify_frame * id)85920b09c29SAndy Yan static u32 mvs_94xx_make_att_info(struct sas_identify_frame *id)
86020b09c29SAndy Yan {
86120b09c29SAndy Yan 	return mvs_94xx_make_dev_info(id);
86220b09c29SAndy Yan }
86320b09c29SAndy Yan 
mvs_94xx_fix_phy_info(struct mvs_info * mvi,int i,struct sas_identify_frame * id)86420b09c29SAndy Yan static void mvs_94xx_fix_phy_info(struct mvs_info *mvi, int i,
86520b09c29SAndy Yan 				struct sas_identify_frame *id)
86620b09c29SAndy Yan {
86720b09c29SAndy Yan 	struct mvs_phy *phy = &mvi->phy[i];
86820b09c29SAndy Yan 	struct asd_sas_phy *sas_phy = &phy->sas_phy;
86920b09c29SAndy Yan 	mv_dprintk("get all reg link rate is 0x%x\n", phy->phy_status);
87020b09c29SAndy Yan 	sas_phy->linkrate =
87120b09c29SAndy Yan 		(phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >>
87220b09c29SAndy Yan 			PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET;
87320b09c29SAndy Yan 	sas_phy->linkrate += 0x8;
87420b09c29SAndy Yan 	mv_dprintk("get link rate is %d\n", sas_phy->linkrate);
87520b09c29SAndy Yan 	phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
87620b09c29SAndy Yan 	phy->maximum_linkrate = SAS_LINK_RATE_6_0_GBPS;
87720b09c29SAndy Yan 	mvs_94xx_get_dev_identify_frame(mvi, i, id);
87820b09c29SAndy Yan 	phy->dev_info = mvs_94xx_make_dev_info(id);
87920b09c29SAndy Yan 
88020b09c29SAndy Yan 	if (phy->phy_type & PORT_TYPE_SAS) {
88120b09c29SAndy Yan 		mvs_94xx_get_att_identify_frame(mvi, i, id);
88220b09c29SAndy Yan 		phy->att_dev_info = mvs_94xx_make_att_info(id);
88320b09c29SAndy Yan 		phy->att_dev_sas_addr = *(u64 *)id->sas_addr;
88420b09c29SAndy Yan 	} else {
88520b09c29SAndy Yan 		phy->att_dev_info = PORT_DEV_STP_TRGT | 1;
88620b09c29SAndy Yan 	}
88720b09c29SAndy Yan 
888477f6d19SXiangliang Yu 	/* enable spin up bit */
889477f6d19SXiangliang Yu 	mvs_write_port_cfg_addr(mvi, i, PHYR_PHY_STAT);
890477f6d19SXiangliang Yu 	mvs_write_port_cfg_data(mvi, i, 0x04);
891477f6d19SXiangliang Yu 
89220b09c29SAndy Yan }
89320b09c29SAndy Yan 
mvs_94xx_phy_set_link_rate(struct mvs_info * mvi,u32 phy_id,struct sas_phy_linkrates * rates)89414bf41dcSBaoyou Xie static void mvs_94xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id,
89520b09c29SAndy Yan 				       struct sas_phy_linkrates *rates)
89620b09c29SAndy Yan {
897a4632aaeSXiangliang Yu 	u32 lrmax = 0;
898a4632aaeSXiangliang Yu 	u32 tmp;
899a4632aaeSXiangliang Yu 
900a4632aaeSXiangliang Yu 	tmp = mvs_read_phy_ctl(mvi, phy_id);
901a4632aaeSXiangliang Yu 	lrmax = (rates->maximum_linkrate - SAS_LINK_RATE_1_5_GBPS) << 12;
902a4632aaeSXiangliang Yu 
903a4632aaeSXiangliang Yu 	if (lrmax) {
904a4632aaeSXiangliang Yu 		tmp &= ~(0x3 << 12);
905a4632aaeSXiangliang Yu 		tmp |= lrmax;
906a4632aaeSXiangliang Yu 	}
907a4632aaeSXiangliang Yu 	mvs_write_phy_ctl(mvi, phy_id, tmp);
908a4632aaeSXiangliang Yu 	mvs_94xx_phy_reset(mvi, phy_id, PHY_RST_HARD);
90920b09c29SAndy Yan }
91020b09c29SAndy Yan 
mvs_94xx_clear_active_cmds(struct mvs_info * mvi)91120b09c29SAndy Yan static void mvs_94xx_clear_active_cmds(struct mvs_info *mvi)
91220b09c29SAndy Yan {
91320b09c29SAndy Yan 	u32 tmp;
91420b09c29SAndy Yan 	void __iomem *regs = mvi->regs;
91520b09c29SAndy Yan 	tmp = mr32(MVS_STP_REG_SET_0);
91620b09c29SAndy Yan 	mw32(MVS_STP_REG_SET_0, 0);
91720b09c29SAndy Yan 	mw32(MVS_STP_REG_SET_0, tmp);
91820b09c29SAndy Yan 	tmp = mr32(MVS_STP_REG_SET_1);
91920b09c29SAndy Yan 	mw32(MVS_STP_REG_SET_1, 0);
92020b09c29SAndy Yan 	mw32(MVS_STP_REG_SET_1, tmp);
92120b09c29SAndy Yan }
92220b09c29SAndy Yan 
92320b09c29SAndy Yan 
mvs_94xx_spi_read_data(struct mvs_info * mvi)92414bf41dcSBaoyou Xie static u32 mvs_94xx_spi_read_data(struct mvs_info *mvi)
92520b09c29SAndy Yan {
92620b09c29SAndy Yan 	void __iomem *regs = mvi->regs_ex - 0x10200;
92720b09c29SAndy Yan 	return mr32(SPI_RD_DATA_REG_94XX);
92820b09c29SAndy Yan }
92920b09c29SAndy Yan 
mvs_94xx_spi_write_data(struct mvs_info * mvi,u32 data)93014bf41dcSBaoyou Xie static void mvs_94xx_spi_write_data(struct mvs_info *mvi, u32 data)
93120b09c29SAndy Yan {
93220b09c29SAndy Yan 	void __iomem *regs = mvi->regs_ex - 0x10200;
933f2c43a62SColin Ian King 
93420b09c29SAndy Yan 	mw32(SPI_RD_DATA_REG_94XX, data);
93520b09c29SAndy Yan }
93620b09c29SAndy Yan 
93720b09c29SAndy Yan 
mvs_94xx_spi_buildcmd(struct mvs_info * mvi,u32 * dwCmd,u8 cmd,u8 read,u8 length,u32 addr)93814bf41dcSBaoyou Xie static int mvs_94xx_spi_buildcmd(struct mvs_info *mvi,
93920b09c29SAndy Yan 				 u32      *dwCmd,
94020b09c29SAndy Yan 				 u8       cmd,
94120b09c29SAndy Yan 				 u8       read,
94220b09c29SAndy Yan 				 u8       length,
94320b09c29SAndy Yan 				 u32      addr
94420b09c29SAndy Yan 				)
94520b09c29SAndy Yan {
94620b09c29SAndy Yan 	void __iomem *regs = mvi->regs_ex - 0x10200;
94720b09c29SAndy Yan 	u32  dwTmp;
94820b09c29SAndy Yan 
94920b09c29SAndy Yan 	dwTmp = ((u32)cmd << 8) | ((u32)length << 4);
95020b09c29SAndy Yan 	if (read)
95120b09c29SAndy Yan 		dwTmp |= SPI_CTRL_READ_94XX;
95220b09c29SAndy Yan 
95320b09c29SAndy Yan 	if (addr != MV_MAX_U32) {
95420b09c29SAndy Yan 		mw32(SPI_ADDR_REG_94XX, (addr & 0x0003FFFFL));
95520b09c29SAndy Yan 		dwTmp |= SPI_ADDR_VLD_94XX;
95620b09c29SAndy Yan 	}
95720b09c29SAndy Yan 
95820b09c29SAndy Yan 	*dwCmd = dwTmp;
95920b09c29SAndy Yan 	return 0;
96020b09c29SAndy Yan }
96120b09c29SAndy Yan 
96220b09c29SAndy Yan 
mvs_94xx_spi_issuecmd(struct mvs_info * mvi,u32 cmd)96314bf41dcSBaoyou Xie static int mvs_94xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd)
96420b09c29SAndy Yan {
96520b09c29SAndy Yan 	void __iomem *regs = mvi->regs_ex - 0x10200;
96620b09c29SAndy Yan 	mw32(SPI_CTRL_REG_94XX, cmd | SPI_CTRL_SpiStart_94XX);
96720b09c29SAndy Yan 
96820b09c29SAndy Yan 	return 0;
96920b09c29SAndy Yan }
97020b09c29SAndy Yan 
mvs_94xx_spi_waitdataready(struct mvs_info * mvi,u32 timeout)97114bf41dcSBaoyou Xie static int mvs_94xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout)
97220b09c29SAndy Yan {
97320b09c29SAndy Yan 	void __iomem *regs = mvi->regs_ex - 0x10200;
97420b09c29SAndy Yan 	u32   i, dwTmp;
97520b09c29SAndy Yan 
97620b09c29SAndy Yan 	for (i = 0; i < timeout; i++) {
97720b09c29SAndy Yan 		dwTmp = mr32(SPI_CTRL_REG_94XX);
97820b09c29SAndy Yan 		if (!(dwTmp & SPI_CTRL_SpiStart_94XX))
97920b09c29SAndy Yan 			return 0;
98020b09c29SAndy Yan 		msleep(10);
98120b09c29SAndy Yan 	}
98220b09c29SAndy Yan 
98320b09c29SAndy Yan 	return -1;
98420b09c29SAndy Yan }
98520b09c29SAndy Yan 
mvs_94xx_fix_dma(struct mvs_info * mvi,u32 phy_mask,int buf_len,int from,void * prd)98614bf41dcSBaoyou Xie static void mvs_94xx_fix_dma(struct mvs_info *mvi, u32 phy_mask,
9878882f081SXiangliang Yu 			     int buf_len, int from, void *prd)
98820b09c29SAndy Yan {
98920b09c29SAndy Yan 	int i;
99020b09c29SAndy Yan 	struct mvs_prd *buf_prd = prd;
9918882f081SXiangliang Yu 	dma_addr_t buf_dma;
99284fbd0ceSXiangliang Yu 	struct mvs_prd_imt im_len;
99384fbd0ceSXiangliang Yu 
99484fbd0ceSXiangliang Yu 	*(u32 *)&im_len = 0;
99520b09c29SAndy Yan 	buf_prd += from;
9968882f081SXiangliang Yu 
99784fbd0ceSXiangliang Yu #define PRD_CHAINED_ENTRY 0x01
9988882f081SXiangliang Yu 	if ((mvi->pdev->revision == VANIR_A0_REV) ||
9998882f081SXiangliang Yu 			(mvi->pdev->revision == VANIR_B0_REV))
10008882f081SXiangliang Yu 		buf_dma = (phy_mask <= 0x08) ?
10018882f081SXiangliang Yu 				mvi->bulk_buffer_dma : mvi->bulk_buffer_dma1;
10028882f081SXiangliang Yu 	else
10038882f081SXiangliang Yu 		return;
10048882f081SXiangliang Yu 
100584fbd0ceSXiangliang Yu 	for (i = from; i < MAX_SG_ENTRY; i++, ++buf_prd) {
100684fbd0ceSXiangliang Yu 		if (i == MAX_SG_ENTRY - 1) {
100784fbd0ceSXiangliang Yu 			buf_prd->addr = cpu_to_le64(virt_to_phys(buf_prd - 1));
100884fbd0ceSXiangliang Yu 			im_len.len = 2;
100984fbd0ceSXiangliang Yu 			im_len.misc_ctl = PRD_CHAINED_ENTRY;
101084fbd0ceSXiangliang Yu 		} else {
101120b09c29SAndy Yan 			buf_prd->addr = cpu_to_le64(buf_dma);
101284fbd0ceSXiangliang Yu 			im_len.len = buf_len;
101384fbd0ceSXiangliang Yu 		}
101484fbd0ceSXiangliang Yu 		buf_prd->im_len = cpu_to_le32(*(u32 *)&im_len);
101520b09c29SAndy Yan 	}
101620b09c29SAndy Yan }
101720b09c29SAndy Yan 
mvs_94xx_tune_interrupt(struct mvs_info * mvi,u32 time)101883c7b61cSXiangliang Yu static void mvs_94xx_tune_interrupt(struct mvs_info *mvi, u32 time)
101983c7b61cSXiangliang Yu {
102083c7b61cSXiangliang Yu 	void __iomem *regs = mvi->regs;
102183c7b61cSXiangliang Yu 	u32 tmp = 0;
1022e144f7efSXiangliang Yu 	/*
1023e144f7efSXiangliang Yu 	 * the max count is 0x1ff, while our max slot is 0x200,
102483c7b61cSXiangliang Yu 	 * it will make count 0.
102583c7b61cSXiangliang Yu 	 */
102683c7b61cSXiangliang Yu 	if (time == 0) {
102783c7b61cSXiangliang Yu 		mw32(MVS_INT_COAL, 0);
102883c7b61cSXiangliang Yu 		mw32(MVS_INT_COAL_TMOUT, 0x10000);
102983c7b61cSXiangliang Yu 	} else {
103083c7b61cSXiangliang Yu 		if (MVS_CHIP_SLOT_SZ > 0x1ff)
103183c7b61cSXiangliang Yu 			mw32(MVS_INT_COAL, 0x1ff|COAL_EN);
103283c7b61cSXiangliang Yu 		else
103383c7b61cSXiangliang Yu 			mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ|COAL_EN);
103483c7b61cSXiangliang Yu 
103583c7b61cSXiangliang Yu 		tmp = 0x10000 | time;
103683c7b61cSXiangliang Yu 		mw32(MVS_INT_COAL_TMOUT, tmp);
103783c7b61cSXiangliang Yu 	}
103883c7b61cSXiangliang Yu 
103983c7b61cSXiangliang Yu }
104083c7b61cSXiangliang Yu 
mvs_94xx_gpio_write(struct mvs_prv_info * mvs_prv,u8 reg_type,u8 reg_index,u8 reg_count,u8 * write_data)1041c56f5f1dSWilfried Weissmann static int mvs_94xx_gpio_write(struct mvs_prv_info *mvs_prv,
1042c56f5f1dSWilfried Weissmann 			u8 reg_type, u8 reg_index,
1043c56f5f1dSWilfried Weissmann 			u8 reg_count, u8 *write_data)
1044c56f5f1dSWilfried Weissmann {
1045c56f5f1dSWilfried Weissmann 	int i;
1046c56f5f1dSWilfried Weissmann 
1047c56f5f1dSWilfried Weissmann 	switch (reg_type) {
1048c56f5f1dSWilfried Weissmann 
1049c56f5f1dSWilfried Weissmann 	case SAS_GPIO_REG_TX_GP:
1050c56f5f1dSWilfried Weissmann 		if (reg_index == 0)
1051c56f5f1dSWilfried Weissmann 			return -EINVAL;
1052c56f5f1dSWilfried Weissmann 
1053c56f5f1dSWilfried Weissmann 		if (reg_count > 1)
1054c56f5f1dSWilfried Weissmann 			return -EINVAL;
1055c56f5f1dSWilfried Weissmann 
1056c56f5f1dSWilfried Weissmann 		if (reg_count == 0)
1057c56f5f1dSWilfried Weissmann 			return 0;
1058c56f5f1dSWilfried Weissmann 
1059c56f5f1dSWilfried Weissmann 		/* maximum supported bits = hosts * 4 drives * 3 bits */
1060c56f5f1dSWilfried Weissmann 		for (i = 0; i < mvs_prv->n_host * 4 * 3; i++) {
1061c56f5f1dSWilfried Weissmann 
1062c56f5f1dSWilfried Weissmann 			/* select host */
1063c56f5f1dSWilfried Weissmann 			struct mvs_info *mvi = mvs_prv->mvi[i/(4*3)];
1064c56f5f1dSWilfried Weissmann 
1065c56f5f1dSWilfried Weissmann 			void __iomem *regs = mvi->regs_ex - 0x10200;
1066c56f5f1dSWilfried Weissmann 
1067c56f5f1dSWilfried Weissmann 			int drive = (i/3) & (4-1); /* drive number on host */
1068e75fba9cSWilfried Weissmann 			int driveshift = drive * 8; /* bit offset of drive */
1069e75fba9cSWilfried Weissmann 			u32 block = ioread32be(regs + MVS_SGPIO_DCTRL +
1070c56f5f1dSWilfried Weissmann 				MVS_SGPIO_HOST_OFFSET * mvi->id);
1071c56f5f1dSWilfried Weissmann 
1072c56f5f1dSWilfried Weissmann 			/*
1073c56f5f1dSWilfried Weissmann 			* if bit is set then create a mask with the first
1074c56f5f1dSWilfried Weissmann 			* bit of the drive set in the mask ...
1075c56f5f1dSWilfried Weissmann 			*/
1076e75fba9cSWilfried Weissmann 			u32 bit = get_unaligned_be32(write_data) & (1 << i) ?
1077e75fba9cSWilfried Weissmann 				1 << driveshift : 0;
1078c56f5f1dSWilfried Weissmann 
1079c56f5f1dSWilfried Weissmann 			/*
1080c56f5f1dSWilfried Weissmann 			* ... and then shift it to the right position based
1081c56f5f1dSWilfried Weissmann 			* on the led type (activity/id/fail)
1082c56f5f1dSWilfried Weissmann 			*/
1083c56f5f1dSWilfried Weissmann 			switch (i%3) {
1084c56f5f1dSWilfried Weissmann 			case 0: /* activity */
1085c56f5f1dSWilfried Weissmann 				block &= ~((0x7 << MVS_SGPIO_DCTRL_ACT_SHIFT)
1086e75fba9cSWilfried Weissmann 					<< driveshift);
1087c56f5f1dSWilfried Weissmann 					/* hardwire activity bit to SOF */
1088c56f5f1dSWilfried Weissmann 				block |= LED_BLINKA_SOF << (
1089c56f5f1dSWilfried Weissmann 					MVS_SGPIO_DCTRL_ACT_SHIFT +
1090e75fba9cSWilfried Weissmann 					driveshift);
1091c56f5f1dSWilfried Weissmann 				break;
1092c56f5f1dSWilfried Weissmann 			case 1: /* id */
1093c56f5f1dSWilfried Weissmann 				block &= ~((0x3 << MVS_SGPIO_DCTRL_LOC_SHIFT)
1094e75fba9cSWilfried Weissmann 					<< driveshift);
1095c56f5f1dSWilfried Weissmann 				block |= bit << MVS_SGPIO_DCTRL_LOC_SHIFT;
1096c56f5f1dSWilfried Weissmann 				break;
1097c56f5f1dSWilfried Weissmann 			case 2: /* fail */
1098c56f5f1dSWilfried Weissmann 				block &= ~((0x7 << MVS_SGPIO_DCTRL_ERR_SHIFT)
1099e75fba9cSWilfried Weissmann 					<< driveshift);
1100c56f5f1dSWilfried Weissmann 				block |= bit << MVS_SGPIO_DCTRL_ERR_SHIFT;
1101c56f5f1dSWilfried Weissmann 				break;
1102c56f5f1dSWilfried Weissmann 			}
1103c56f5f1dSWilfried Weissmann 
1104e75fba9cSWilfried Weissmann 			iowrite32be(block,
1105e75fba9cSWilfried Weissmann 				regs + MVS_SGPIO_DCTRL +
1106e75fba9cSWilfried Weissmann 				MVS_SGPIO_HOST_OFFSET * mvi->id);
1107c56f5f1dSWilfried Weissmann 
1108c56f5f1dSWilfried Weissmann 		}
1109c56f5f1dSWilfried Weissmann 
1110c56f5f1dSWilfried Weissmann 		return reg_count;
1111c56f5f1dSWilfried Weissmann 
1112c56f5f1dSWilfried Weissmann 	case SAS_GPIO_REG_TX:
1113c56f5f1dSWilfried Weissmann 		if (reg_index + reg_count > mvs_prv->n_host)
1114c56f5f1dSWilfried Weissmann 			return -EINVAL;
1115c56f5f1dSWilfried Weissmann 
1116c56f5f1dSWilfried Weissmann 		for (i = 0; i < reg_count; i++) {
1117c56f5f1dSWilfried Weissmann 			struct mvs_info *mvi = mvs_prv->mvi[i+reg_index];
1118c56f5f1dSWilfried Weissmann 			void __iomem *regs = mvi->regs_ex - 0x10200;
1119c56f5f1dSWilfried Weissmann 
1120c56f5f1dSWilfried Weissmann 			mw32(MVS_SGPIO_DCTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
1121e75fba9cSWilfried Weissmann 				((u32 *) write_data)[i]);
1122c56f5f1dSWilfried Weissmann 		}
1123c56f5f1dSWilfried Weissmann 		return reg_count;
1124c56f5f1dSWilfried Weissmann 	}
1125c56f5f1dSWilfried Weissmann 	return -ENOSYS;
1126c56f5f1dSWilfried Weissmann }
1127c56f5f1dSWilfried Weissmann 
112820b09c29SAndy Yan const struct mvs_dispatch mvs_94xx_dispatch = {
112920b09c29SAndy Yan 	"mv94xx",
113020b09c29SAndy Yan 	mvs_94xx_init,
113120b09c29SAndy Yan 	NULL,
113220b09c29SAndy Yan 	mvs_94xx_ioremap,
113320b09c29SAndy Yan 	mvs_94xx_iounmap,
113420b09c29SAndy Yan 	mvs_94xx_isr,
113520b09c29SAndy Yan 	mvs_94xx_isr_status,
113620b09c29SAndy Yan 	mvs_94xx_interrupt_enable,
113720b09c29SAndy Yan 	mvs_94xx_interrupt_disable,
113820b09c29SAndy Yan 	mvs_read_phy_ctl,
113920b09c29SAndy Yan 	mvs_write_phy_ctl,
114020b09c29SAndy Yan 	mvs_read_port_cfg_data,
114120b09c29SAndy Yan 	mvs_write_port_cfg_data,
114220b09c29SAndy Yan 	mvs_write_port_cfg_addr,
114320b09c29SAndy Yan 	mvs_read_port_vsr_data,
114420b09c29SAndy Yan 	mvs_write_port_vsr_data,
114520b09c29SAndy Yan 	mvs_write_port_vsr_addr,
114620b09c29SAndy Yan 	mvs_read_port_irq_stat,
114720b09c29SAndy Yan 	mvs_write_port_irq_stat,
114820b09c29SAndy Yan 	mvs_read_port_irq_mask,
114920b09c29SAndy Yan 	mvs_write_port_irq_mask,
115020b09c29SAndy Yan 	mvs_94xx_command_active,
11519dc9fd94SSrinivas 	mvs_94xx_clear_srs_irq,
115220b09c29SAndy Yan 	mvs_94xx_issue_stop,
115320b09c29SAndy Yan 	mvs_start_delivery,
115420b09c29SAndy Yan 	mvs_rx_update,
115520b09c29SAndy Yan 	mvs_int_full,
115620b09c29SAndy Yan 	mvs_94xx_assign_reg_set,
115720b09c29SAndy Yan 	mvs_94xx_free_reg_set,
115820b09c29SAndy Yan 	mvs_get_prd_size,
115920b09c29SAndy Yan 	mvs_get_prd_count,
116020b09c29SAndy Yan 	mvs_94xx_make_prd,
116120b09c29SAndy Yan 	mvs_94xx_detect_porttype,
116220b09c29SAndy Yan 	mvs_94xx_oob_done,
116320b09c29SAndy Yan 	mvs_94xx_fix_phy_info,
116420b09c29SAndy Yan 	NULL,
116520b09c29SAndy Yan 	mvs_94xx_phy_set_link_rate,
116620b09c29SAndy Yan 	mvs_hw_max_link_rate,
116720b09c29SAndy Yan 	mvs_94xx_phy_disable,
116820b09c29SAndy Yan 	mvs_94xx_phy_enable,
116920b09c29SAndy Yan 	mvs_94xx_phy_reset,
117020b09c29SAndy Yan 	NULL,
117120b09c29SAndy Yan 	mvs_94xx_clear_active_cmds,
117220b09c29SAndy Yan 	mvs_94xx_spi_read_data,
117320b09c29SAndy Yan 	mvs_94xx_spi_write_data,
117420b09c29SAndy Yan 	mvs_94xx_spi_buildcmd,
117520b09c29SAndy Yan 	mvs_94xx_spi_issuecmd,
117620b09c29SAndy Yan 	mvs_94xx_spi_waitdataready,
117720b09c29SAndy Yan 	mvs_94xx_fix_dma,
117883c7b61cSXiangliang Yu 	mvs_94xx_tune_interrupt,
1179534ff101SXiangliang Yu 	mvs_94xx_non_spec_ncq_error,
1180c56f5f1dSWilfried Weissmann 	mvs_94xx_gpio_write,
118120b09c29SAndy Yan };
118220b09c29SAndy Yan 
1183