diff options
Diffstat (limited to 'drivers/scsi/mvsas/mv_64xx.c')
-rw-r--r-- | drivers/scsi/mvsas/mv_64xx.c | 785 |
1 files changed, 697 insertions, 88 deletions
diff --git a/drivers/scsi/mvsas/mv_64xx.c b/drivers/scsi/mvsas/mv_64xx.c index 697806c856af..10a5077b6aed 100644 --- a/drivers/scsi/mvsas/mv_64xx.c +++ b/drivers/scsi/mvsas/mv_64xx.c | |||
@@ -1,184 +1,793 @@ | |||
1 | /* | 1 | /* |
2 | mv_64xx.c - Marvell 88SE6440 SAS/SATA support | 2 | * Marvell 88SE64xx hardware specific |
3 | 3 | * | |
4 | Copyright 2007 Red Hat, Inc. | 4 | * Copyright 2007 Red Hat, Inc. |
5 | Copyright 2008 Marvell. <kewei@marvell.com> | 5 | * Copyright 2008 Marvell. <kewei@marvell.com> |
6 | 6 | * | |
7 | This program is free software; you can redistribute it and/or | 7 | * This file is licensed under GPLv2. |
8 | modify it under the terms of the GNU General Public License as | 8 | * |
9 | published by the Free Software Foundation; either version 2, | 9 | * This program is free software; you can redistribute it and/or |
10 | or (at your option) any later version. | 10 | * modify it under the terms of the GNU General Public License as |
11 | 11 | * published by the Free Software Foundation; version 2 of the | |
12 | This program is distributed in the hope that it will be useful, | 12 | * License. |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty | 13 | * |
14 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | 14 | * This program is distributed in the hope that it will be useful, |
15 | See the GNU General Public License for more details. | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | You should have received a copy of the GNU General Public | 17 | * General Public License for more details. |
18 | License along with this program; see the file COPYING. If not, | 18 | * |
19 | write to the Free Software Foundation, 675 Mass Ave, Cambridge, | 19 | * You should have received a copy of the GNU General Public License |
20 | MA 02139, USA. | 20 | * along with this program; if not, write to the Free Software |
21 | 21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
22 | */ | 22 | * USA |
23 | */ | ||
23 | 24 | ||
24 | #include "mv_sas.h" | 25 | #include "mv_sas.h" |
25 | #include "mv_64xx.h" | 26 | #include "mv_64xx.h" |
26 | #include "mv_chips.h" | 27 | #include "mv_chips.h" |
27 | 28 | ||
28 | void mvs_detect_porttype(struct mvs_info *mvi, int i) | 29 | static void mvs_64xx_detect_porttype(struct mvs_info *mvi, int i) |
29 | { | 30 | { |
30 | void __iomem *regs = mvi->regs; | 31 | void __iomem *regs = mvi->regs; |
31 | u32 reg; | 32 | u32 reg; |
32 | struct mvs_phy *phy = &mvi->phy[i]; | 33 | struct mvs_phy *phy = &mvi->phy[i]; |
33 | 34 | ||
34 | /* TODO check & save device type */ | 35 | /* TODO check & save device type */ |
35 | reg = mr32(GBL_PORT_TYPE); | 36 | reg = mr32(MVS_GBL_PORT_TYPE); |
36 | 37 | phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA); | |
37 | if (reg & MODE_SAS_SATA & (1 << i)) | 38 | if (reg & MODE_SAS_SATA & (1 << i)) |
38 | phy->phy_type |= PORT_TYPE_SAS; | 39 | phy->phy_type |= PORT_TYPE_SAS; |
39 | else | 40 | else |
40 | phy->phy_type |= PORT_TYPE_SATA; | 41 | phy->phy_type |= PORT_TYPE_SATA; |
41 | } | 42 | } |
42 | 43 | ||
43 | void mvs_enable_xmt(struct mvs_info *mvi, int PhyId) | 44 | static void __devinit mvs_64xx_enable_xmt(struct mvs_info *mvi, int phy_id) |
44 | { | 45 | { |
45 | void __iomem *regs = mvi->regs; | 46 | void __iomem *regs = mvi->regs; |
46 | u32 tmp; | 47 | u32 tmp; |
47 | 48 | ||
48 | tmp = mr32(PCS); | 49 | tmp = mr32(MVS_PCS); |
49 | if (mvi->chip->n_phy <= 4) | 50 | if (mvi->chip->n_phy <= 4) |
50 | tmp |= 1 << (PhyId + PCS_EN_PORT_XMT_SHIFT); | 51 | tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT); |
52 | else | ||
53 | tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT2); | ||
54 | mw32(MVS_PCS, tmp); | ||
55 | } | ||
56 | |||
57 | static void __devinit mvs_64xx_phy_hacks(struct mvs_info *mvi) | ||
58 | { | ||
59 | void __iomem *regs = mvi->regs; | ||
60 | |||
61 | mvs_phy_hacks(mvi); | ||
62 | |||
63 | if (!(mvi->flags & MVF_FLAG_SOC)) { | ||
64 | /* TEST - for phy decoding error, adjust voltage levels */ | ||
65 | mw32(MVS_P0_VSR_ADDR + 0, 0x8); | ||
66 | mw32(MVS_P0_VSR_DATA + 0, 0x2F0); | ||
67 | |||
68 | mw32(MVS_P0_VSR_ADDR + 8, 0x8); | ||
69 | mw32(MVS_P0_VSR_DATA + 8, 0x2F0); | ||
70 | |||
71 | mw32(MVS_P0_VSR_ADDR + 16, 0x8); | ||
72 | mw32(MVS_P0_VSR_DATA + 16, 0x2F0); | ||
73 | |||
74 | mw32(MVS_P0_VSR_ADDR + 24, 0x8); | ||
75 | mw32(MVS_P0_VSR_DATA + 24, 0x2F0); | ||
76 | } else { | ||
77 | int i; | ||
78 | /* disable auto port detection */ | ||
79 | mw32(MVS_GBL_PORT_TYPE, 0); | ||
80 | for (i = 0; i < mvi->chip->n_phy; i++) { | ||
81 | mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE7); | ||
82 | mvs_write_port_vsr_data(mvi, i, 0x90000000); | ||
83 | mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE9); | ||
84 | mvs_write_port_vsr_data(mvi, i, 0x50f2); | ||
85 | mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE11); | ||
86 | mvs_write_port_vsr_data(mvi, i, 0x0e); | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | |||
91 | static void mvs_64xx_stp_reset(struct mvs_info *mvi, u32 phy_id) | ||
92 | { | ||
93 | void __iomem *regs = mvi->regs; | ||
94 | u32 reg, tmp; | ||
95 | |||
96 | if (!(mvi->flags & MVF_FLAG_SOC)) { | ||
97 | if (phy_id < 4) | ||
98 | pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, ®); | ||
99 | else | ||
100 | pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, ®); | ||
101 | |||
102 | } else | ||
103 | reg = mr32(MVS_PHY_CTL); | ||
104 | |||
105 | tmp = reg; | ||
106 | if (phy_id < 4) | ||
107 | tmp |= (1U << phy_id) << PCTL_LINK_OFFS; | ||
51 | else | 108 | else |
52 | tmp |= 1 << (PhyId + PCS_EN_PORT_XMT_SHIFT2); | 109 | tmp |= (1U << (phy_id - 4)) << PCTL_LINK_OFFS; |
53 | mw32(PCS, tmp); | 110 | |
111 | if (!(mvi->flags & MVF_FLAG_SOC)) { | ||
112 | if (phy_id < 4) { | ||
113 | pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp); | ||
114 | mdelay(10); | ||
115 | pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, reg); | ||
116 | } else { | ||
117 | pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, tmp); | ||
118 | mdelay(10); | ||
119 | pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, reg); | ||
120 | } | ||
121 | } else { | ||
122 | mw32(MVS_PHY_CTL, tmp); | ||
123 | mdelay(10); | ||
124 | mw32(MVS_PHY_CTL, reg); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | static void mvs_64xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard) | ||
129 | { | ||
130 | u32 tmp; | ||
131 | tmp = mvs_read_port_irq_stat(mvi, phy_id); | ||
132 | tmp &= ~PHYEV_RDY_CH; | ||
133 | mvs_write_port_irq_stat(mvi, phy_id, tmp); | ||
134 | tmp = mvs_read_phy_ctl(mvi, phy_id); | ||
135 | if (hard) | ||
136 | tmp |= PHY_RST_HARD; | ||
137 | else | ||
138 | tmp |= PHY_RST; | ||
139 | mvs_write_phy_ctl(mvi, phy_id, tmp); | ||
140 | if (hard) { | ||
141 | do { | ||
142 | tmp = mvs_read_phy_ctl(mvi, phy_id); | ||
143 | } while (tmp & PHY_RST_HARD); | ||
144 | } | ||
145 | } | ||
146 | |||
147 | static int __devinit mvs_64xx_chip_reset(struct mvs_info *mvi) | ||
148 | { | ||
149 | void __iomem *regs = mvi->regs; | ||
150 | u32 tmp; | ||
151 | int i; | ||
152 | |||
153 | /* make sure interrupts are masked immediately (paranoia) */ | ||
154 | mw32(MVS_GBL_CTL, 0); | ||
155 | tmp = mr32(MVS_GBL_CTL); | ||
156 | |||
157 | /* Reset Controller */ | ||
158 | if (!(tmp & HBA_RST)) { | ||
159 | if (mvi->flags & MVF_PHY_PWR_FIX) { | ||
160 | pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &tmp); | ||
161 | tmp &= ~PCTL_PWR_OFF; | ||
162 | tmp |= PCTL_PHY_DSBL; | ||
163 | pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp); | ||
164 | |||
165 | pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &tmp); | ||
166 | tmp &= ~PCTL_PWR_OFF; | ||
167 | tmp |= PCTL_PHY_DSBL; | ||
168 | pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, tmp); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | /* make sure interrupts are masked immediately (paranoia) */ | ||
173 | mw32(MVS_GBL_CTL, 0); | ||
174 | tmp = mr32(MVS_GBL_CTL); | ||
175 | |||
176 | /* Reset Controller */ | ||
177 | if (!(tmp & HBA_RST)) { | ||
178 | /* global reset, incl. COMRESET/H_RESET_N (self-clearing) */ | ||
179 | mw32_f(MVS_GBL_CTL, HBA_RST); | ||
180 | } | ||
181 | |||
182 | /* wait for reset to finish; timeout is just a guess */ | ||
183 | i = 1000; | ||
184 | while (i-- > 0) { | ||
185 | msleep(10); | ||
186 | |||
187 | if (!(mr32(MVS_GBL_CTL) & HBA_RST)) | ||
188 | break; | ||
189 | } | ||
190 | if (mr32(MVS_GBL_CTL) & HBA_RST) { | ||
191 | dev_printk(KERN_ERR, mvi->dev, "HBA reset failed\n"); | ||
192 | return -EBUSY; | ||
193 | } | ||
194 | return 0; | ||
54 | } | 195 | } |
55 | 196 | ||
56 | void __devinit mvs_phy_hacks(struct mvs_info *mvi) | 197 | static void mvs_64xx_phy_disable(struct mvs_info *mvi, u32 phy_id) |
57 | { | 198 | { |
58 | void __iomem *regs = mvi->regs; | 199 | void __iomem *regs = mvi->regs; |
59 | u32 tmp; | 200 | u32 tmp; |
201 | if (!(mvi->flags & MVF_FLAG_SOC)) { | ||
202 | u32 offs; | ||
203 | if (phy_id < 4) | ||
204 | offs = PCR_PHY_CTL; | ||
205 | else { | ||
206 | offs = PCR_PHY_CTL2; | ||
207 | phy_id -= 4; | ||
208 | } | ||
209 | pci_read_config_dword(mvi->pdev, offs, &tmp); | ||
210 | tmp |= 1U << (PCTL_PHY_DSBL_OFFS + phy_id); | ||
211 | pci_write_config_dword(mvi->pdev, offs, tmp); | ||
212 | } else { | ||
213 | tmp = mr32(MVS_PHY_CTL); | ||
214 | tmp |= 1U << (PCTL_PHY_DSBL_OFFS + phy_id); | ||
215 | mw32(MVS_PHY_CTL, tmp); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | static void mvs_64xx_phy_enable(struct mvs_info *mvi, u32 phy_id) | ||
220 | { | ||
221 | void __iomem *regs = mvi->regs; | ||
222 | u32 tmp; | ||
223 | if (!(mvi->flags & MVF_FLAG_SOC)) { | ||
224 | u32 offs; | ||
225 | if (phy_id < 4) | ||
226 | offs = PCR_PHY_CTL; | ||
227 | else { | ||
228 | offs = PCR_PHY_CTL2; | ||
229 | phy_id -= 4; | ||
230 | } | ||
231 | pci_read_config_dword(mvi->pdev, offs, &tmp); | ||
232 | tmp &= ~(1U << (PCTL_PHY_DSBL_OFFS + phy_id)); | ||
233 | pci_write_config_dword(mvi->pdev, offs, tmp); | ||
234 | } else { | ||
235 | tmp = mr32(MVS_PHY_CTL); | ||
236 | tmp &= ~(1U << (PCTL_PHY_DSBL_OFFS + phy_id)); | ||
237 | mw32(MVS_PHY_CTL, tmp); | ||
238 | } | ||
239 | } | ||
60 | 240 | ||
61 | /* workaround for SATA R-ERR, to ignore phy glitch */ | 241 | static int __devinit mvs_64xx_init(struct mvs_info *mvi) |
62 | tmp = mvs_cr32(regs, CMD_PHY_TIMER); | 242 | { |
63 | tmp &= ~(1 << 9); | 243 | void __iomem *regs = mvi->regs; |
64 | tmp |= (1 << 10); | 244 | int i; |
65 | mvs_cw32(regs, CMD_PHY_TIMER, tmp); | 245 | u32 tmp, cctl; |
246 | |||
247 | if (mvi->pdev && mvi->pdev->revision == 0) | ||
248 | mvi->flags |= MVF_PHY_PWR_FIX; | ||
249 | if (!(mvi->flags & MVF_FLAG_SOC)) { | ||
250 | mvs_show_pcie_usage(mvi); | ||
251 | tmp = mvs_64xx_chip_reset(mvi); | ||
252 | if (tmp) | ||
253 | return tmp; | ||
254 | } else { | ||
255 | tmp = mr32(MVS_PHY_CTL); | ||
256 | tmp &= ~PCTL_PWR_OFF; | ||
257 | tmp |= PCTL_PHY_DSBL; | ||
258 | mw32(MVS_PHY_CTL, tmp); | ||
259 | } | ||
66 | 260 | ||
67 | /* enable retry 127 times */ | 261 | /* Init Chip */ |
68 | mvs_cw32(regs, CMD_SAS_CTL1, 0x7f7f); | 262 | /* make sure RST is set; HBA_RST /should/ have done that for us */ |
263 | cctl = mr32(MVS_CTL) & 0xFFFF; | ||
264 | if (cctl & CCTL_RST) | ||
265 | cctl &= ~CCTL_RST; | ||
266 | else | ||
267 | mw32_f(MVS_CTL, cctl | CCTL_RST); | ||
268 | |||
269 | if (!(mvi->flags & MVF_FLAG_SOC)) { | ||
270 | /* write to device control _AND_ device status register */ | ||
271 | pci_read_config_dword(mvi->pdev, PCR_DEV_CTRL, &tmp); | ||
272 | tmp &= ~PRD_REQ_MASK; | ||
273 | tmp |= PRD_REQ_SIZE; | ||
274 | pci_write_config_dword(mvi->pdev, PCR_DEV_CTRL, tmp); | ||
275 | |||
276 | pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &tmp); | ||
277 | tmp &= ~PCTL_PWR_OFF; | ||
278 | tmp &= ~PCTL_PHY_DSBL; | ||
279 | pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp); | ||
280 | |||
281 | pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &tmp); | ||
282 | tmp &= PCTL_PWR_OFF; | ||
283 | tmp &= ~PCTL_PHY_DSBL; | ||
284 | pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, tmp); | ||
285 | } else { | ||
286 | tmp = mr32(MVS_PHY_CTL); | ||
287 | tmp &= ~PCTL_PWR_OFF; | ||
288 | tmp |= PCTL_COM_ON; | ||
289 | tmp &= ~PCTL_PHY_DSBL; | ||
290 | tmp |= PCTL_LINK_RST; | ||
291 | mw32(MVS_PHY_CTL, tmp); | ||
292 | msleep(100); | ||
293 | tmp &= ~PCTL_LINK_RST; | ||
294 | mw32(MVS_PHY_CTL, tmp); | ||
295 | msleep(100); | ||
296 | } | ||
69 | 297 | ||
70 | /* extend open frame timeout to max */ | 298 | /* reset control */ |
71 | tmp = mvs_cr32(regs, CMD_SAS_CTL0); | 299 | mw32(MVS_PCS, 0); /* MVS_PCS */ |
72 | tmp &= ~0xffff; | 300 | /* init phys */ |
73 | tmp |= 0x3fff; | 301 | mvs_64xx_phy_hacks(mvi); |
74 | mvs_cw32(regs, CMD_SAS_CTL0, tmp); | ||
75 | 302 | ||
76 | /* workaround for WDTIMEOUT , set to 550 ms */ | 303 | /* enable auto port detection */ |
77 | mvs_cw32(regs, CMD_WD_TIMER, 0x86470); | 304 | mw32(MVS_GBL_PORT_TYPE, MODE_AUTO_DET_EN); |
78 | 305 | ||
79 | /* not to halt for different port op during wideport link change */ | 306 | mw32(MVS_CMD_LIST_LO, mvi->slot_dma); |
80 | mvs_cw32(regs, CMD_APP_ERR_CONFIG, 0xffefbf7d); | 307 | mw32(MVS_CMD_LIST_HI, (mvi->slot_dma >> 16) >> 16); |
81 | 308 | ||
82 | /* workaround for Seagate disk not-found OOB sequence, recv | 309 | mw32(MVS_RX_FIS_LO, mvi->rx_fis_dma); |
83 | * COMINIT before sending out COMWAKE */ | 310 | mw32(MVS_RX_FIS_HI, (mvi->rx_fis_dma >> 16) >> 16); |
84 | tmp = mvs_cr32(regs, CMD_PHY_MODE_21); | ||
85 | tmp &= 0x0000ffff; | ||
86 | tmp |= 0x00fa0000; | ||
87 | mvs_cw32(regs, CMD_PHY_MODE_21, tmp); | ||
88 | 311 | ||
89 | tmp = mvs_cr32(regs, CMD_PHY_TIMER); | 312 | mw32(MVS_TX_CFG, MVS_CHIP_SLOT_SZ); |
90 | tmp &= 0x1fffffff; | 313 | mw32(MVS_TX_LO, mvi->tx_dma); |
91 | tmp |= (2U << 29); /* 8 ms retry */ | 314 | mw32(MVS_TX_HI, (mvi->tx_dma >> 16) >> 16); |
92 | mvs_cw32(regs, CMD_PHY_TIMER, tmp); | ||
93 | 315 | ||
94 | /* TEST - for phy decoding error, adjust voltage levels */ | 316 | mw32(MVS_RX_CFG, MVS_RX_RING_SZ); |
95 | mw32(P0_VSR_ADDR + 0, 0x8); | 317 | mw32(MVS_RX_LO, mvi->rx_dma); |
96 | mw32(P0_VSR_DATA + 0, 0x2F0); | 318 | mw32(MVS_RX_HI, (mvi->rx_dma >> 16) >> 16); |
97 | 319 | ||
98 | mw32(P0_VSR_ADDR + 8, 0x8); | 320 | for (i = 0; i < mvi->chip->n_phy; i++) { |
99 | mw32(P0_VSR_DATA + 8, 0x2F0); | 321 | /* set phy local SAS address */ |
322 | /* should set little endian SAS address to 64xx chip */ | ||
323 | mvs_set_sas_addr(mvi, i, PHYR_ADDR_LO, PHYR_ADDR_HI, | ||
324 | cpu_to_be64(mvi->phy[i].dev_sas_addr)); | ||
100 | 325 | ||
101 | mw32(P0_VSR_ADDR + 16, 0x8); | 326 | mvs_64xx_enable_xmt(mvi, i); |
102 | mw32(P0_VSR_DATA + 16, 0x2F0); | ||
103 | 327 | ||
104 | mw32(P0_VSR_ADDR + 24, 0x8); | 328 | mvs_64xx_phy_reset(mvi, i, 1); |
105 | mw32(P0_VSR_DATA + 24, 0x2F0); | 329 | msleep(500); |
330 | mvs_64xx_detect_porttype(mvi, i); | ||
331 | } | ||
332 | if (mvi->flags & MVF_FLAG_SOC) { | ||
333 | /* set select registers */ | ||
334 | writel(0x0E008000, regs + 0x000); | ||
335 | writel(0x59000008, regs + 0x004); | ||
336 | writel(0x20, regs + 0x008); | ||
337 | writel(0x20, regs + 0x00c); | ||
338 | writel(0x20, regs + 0x010); | ||
339 | writel(0x20, regs + 0x014); | ||
340 | writel(0x20, regs + 0x018); | ||
341 | writel(0x20, regs + 0x01c); | ||
342 | } | ||
343 | for (i = 0; i < mvi->chip->n_phy; i++) { | ||
344 | /* clear phy int status */ | ||
345 | tmp = mvs_read_port_irq_stat(mvi, i); | ||
346 | tmp &= ~PHYEV_SIG_FIS; | ||
347 | mvs_write_port_irq_stat(mvi, i, tmp); | ||
348 | |||
349 | /* set phy int mask */ | ||
350 | tmp = PHYEV_RDY_CH | PHYEV_BROAD_CH | PHYEV_UNASSOC_FIS | | ||
351 | PHYEV_ID_DONE | PHYEV_DCDR_ERR | PHYEV_CRC_ERR | | ||
352 | PHYEV_DEC_ERR; | ||
353 | mvs_write_port_irq_mask(mvi, i, tmp); | ||
354 | |||
355 | msleep(100); | ||
356 | mvs_update_phyinfo(mvi, i, 1); | ||
357 | } | ||
106 | 358 | ||
359 | /* FIXME: update wide port bitmaps */ | ||
360 | |||
361 | /* little endian for open address and command table, etc. */ | ||
362 | /* | ||
363 | * it seems that ( from the spec ) turning on big-endian won't | ||
364 | * do us any good on big-endian machines, need further confirmation | ||
365 | */ | ||
366 | cctl = mr32(MVS_CTL); | ||
367 | cctl |= CCTL_ENDIAN_CMD; | ||
368 | cctl |= CCTL_ENDIAN_DATA; | ||
369 | cctl &= ~CCTL_ENDIAN_OPEN; | ||
370 | cctl |= CCTL_ENDIAN_RSP; | ||
371 | mw32_f(MVS_CTL, cctl); | ||
372 | |||
373 | /* reset CMD queue */ | ||
374 | tmp = mr32(MVS_PCS); | ||
375 | tmp |= PCS_CMD_RST; | ||
376 | mw32(MVS_PCS, tmp); | ||
377 | /* interrupt coalescing may cause missing HW interrput in some case, | ||
378 | * and the max count is 0x1ff, while our max slot is 0x200, | ||
379 | * it will make count 0. | ||
380 | */ | ||
381 | tmp = 0; | ||
382 | mw32(MVS_INT_COAL, tmp); | ||
383 | |||
384 | tmp = 0x100; | ||
385 | mw32(MVS_INT_COAL_TMOUT, tmp); | ||
386 | |||
387 | /* ladies and gentlemen, start your engines */ | ||
388 | mw32(MVS_TX_CFG, 0); | ||
389 | mw32(MVS_TX_CFG, MVS_CHIP_SLOT_SZ | TX_EN); | ||
390 | mw32(MVS_RX_CFG, MVS_RX_RING_SZ | RX_EN); | ||
391 | /* enable CMD/CMPL_Q/RESP mode */ | ||
392 | mw32(MVS_PCS, PCS_SATA_RETRY | PCS_FIS_RX_EN | | ||
393 | PCS_CMD_EN | PCS_CMD_STOP_ERR); | ||
394 | |||
395 | /* enable completion queue interrupt */ | ||
396 | tmp = (CINT_PORT_MASK | CINT_DONE | CINT_MEM | CINT_SRS | CINT_CI_STOP | | ||
397 | CINT_DMA_PCIE); | ||
398 | |||
399 | mw32(MVS_INT_MASK, tmp); | ||
400 | |||
401 | /* Enable SRS interrupt */ | ||
402 | mw32(MVS_INT_MASK_SRS_0, 0xFFFF); | ||
403 | |||
404 | return 0; | ||
107 | } | 405 | } |
108 | 406 | ||
109 | void mvs_hba_interrupt_enable(struct mvs_info *mvi) | 407 | static int mvs_64xx_ioremap(struct mvs_info *mvi) |
408 | { | ||
409 | if (!mvs_ioremap(mvi, 4, 2)) | ||
410 | return 0; | ||
411 | return -1; | ||
412 | } | ||
413 | |||
414 | static void mvs_64xx_iounmap(struct mvs_info *mvi) | ||
415 | { | ||
416 | mvs_iounmap(mvi->regs); | ||
417 | mvs_iounmap(mvi->regs_ex); | ||
418 | } | ||
419 | |||
420 | static void mvs_64xx_interrupt_enable(struct mvs_info *mvi) | ||
110 | { | 421 | { |
111 | void __iomem *regs = mvi->regs; | 422 | void __iomem *regs = mvi->regs; |
112 | u32 tmp; | 423 | u32 tmp; |
113 | 424 | ||
114 | tmp = mr32(GBL_CTL); | 425 | tmp = mr32(MVS_GBL_CTL); |
426 | mw32(MVS_GBL_CTL, tmp | INT_EN); | ||
427 | } | ||
115 | 428 | ||
116 | mw32(GBL_CTL, tmp | INT_EN); | 429 | static void mvs_64xx_interrupt_disable(struct mvs_info *mvi) |
430 | { | ||
431 | void __iomem *regs = mvi->regs; | ||
432 | u32 tmp; | ||
433 | |||
434 | tmp = mr32(MVS_GBL_CTL); | ||
435 | mw32(MVS_GBL_CTL, tmp & ~INT_EN); | ||
117 | } | 436 | } |
118 | 437 | ||
119 | void mvs_hba_interrupt_disable(struct mvs_info *mvi) | 438 | static u32 mvs_64xx_isr_status(struct mvs_info *mvi, int irq) |
120 | { | 439 | { |
121 | void __iomem *regs = mvi->regs; | 440 | void __iomem *regs = mvi->regs; |
441 | u32 stat; | ||
442 | |||
443 | if (!(mvi->flags & MVF_FLAG_SOC)) { | ||
444 | stat = mr32(MVS_GBL_INT_STAT); | ||
445 | |||
446 | if (stat == 0 || stat == 0xffffffff) | ||
447 | return 0; | ||
448 | } else | ||
449 | stat = 1; | ||
450 | return stat; | ||
451 | } | ||
452 | |||
453 | static irqreturn_t mvs_64xx_isr(struct mvs_info *mvi, int irq, u32 stat) | ||
454 | { | ||
455 | void __iomem *regs = mvi->regs; | ||
456 | |||
457 | /* clear CMD_CMPLT ASAP */ | ||
458 | mw32_f(MVS_INT_STAT, CINT_DONE); | ||
459 | #ifndef MVS_USE_TASKLET | ||
460 | spin_lock(&mvi->lock); | ||
461 | #endif | ||
462 | mvs_int_full(mvi); | ||
463 | #ifndef MVS_USE_TASKLET | ||
464 | spin_unlock(&mvi->lock); | ||
465 | #endif | ||
466 | return IRQ_HANDLED; | ||
467 | } | ||
468 | |||
469 | static void mvs_64xx_command_active(struct mvs_info *mvi, u32 slot_idx) | ||
470 | { | ||
122 | u32 tmp; | 471 | u32 tmp; |
472 | mvs_cw32(mvi, 0x40 + (slot_idx >> 3), 1 << (slot_idx % 32)); | ||
473 | mvs_cw32(mvi, 0x00 + (slot_idx >> 3), 1 << (slot_idx % 32)); | ||
474 | do { | ||
475 | tmp = mvs_cr32(mvi, 0x00 + (slot_idx >> 3)); | ||
476 | } while (tmp & 1 << (slot_idx % 32)); | ||
477 | do { | ||
478 | tmp = mvs_cr32(mvi, 0x40 + (slot_idx >> 3)); | ||
479 | } while (tmp & 1 << (slot_idx % 32)); | ||
480 | } | ||
123 | 481 | ||
124 | tmp = mr32(GBL_CTL); | 482 | static void mvs_64xx_issue_stop(struct mvs_info *mvi, enum mvs_port_type type, |
483 | u32 tfs) | ||
484 | { | ||
485 | void __iomem *regs = mvi->regs; | ||
486 | u32 tmp; | ||
125 | 487 | ||
126 | mw32(GBL_CTL, tmp & ~INT_EN); | 488 | if (type == PORT_TYPE_SATA) { |
489 | tmp = mr32(MVS_INT_STAT_SRS_0) | (1U << tfs); | ||
490 | mw32(MVS_INT_STAT_SRS_0, tmp); | ||
491 | } | ||
492 | mw32(MVS_INT_STAT, CINT_CI_STOP); | ||
493 | tmp = mr32(MVS_PCS) | 0xFF00; | ||
494 | mw32(MVS_PCS, tmp); | ||
127 | } | 495 | } |
128 | 496 | ||
129 | void mvs_free_reg_set(struct mvs_info *mvi, struct mvs_port *port) | 497 | static void mvs_64xx_free_reg_set(struct mvs_info *mvi, u8 *tfs) |
130 | { | 498 | { |
131 | void __iomem *regs = mvi->regs; | 499 | void __iomem *regs = mvi->regs; |
132 | u32 tmp, offs; | 500 | u32 tmp, offs; |
133 | u8 *tfs = &port->taskfileset; | ||
134 | 501 | ||
135 | if (*tfs == MVS_ID_NOT_MAPPED) | 502 | if (*tfs == MVS_ID_NOT_MAPPED) |
136 | return; | 503 | return; |
137 | 504 | ||
138 | offs = 1U << ((*tfs & 0x0f) + PCS_EN_SATA_REG_SHIFT); | 505 | offs = 1U << ((*tfs & 0x0f) + PCS_EN_SATA_REG_SHIFT); |
139 | if (*tfs < 16) { | 506 | if (*tfs < 16) { |
140 | tmp = mr32(PCS); | 507 | tmp = mr32(MVS_PCS); |
141 | mw32(PCS, tmp & ~offs); | 508 | mw32(MVS_PCS, tmp & ~offs); |
142 | } else { | 509 | } else { |
143 | tmp = mr32(CTL); | 510 | tmp = mr32(MVS_CTL); |
144 | mw32(CTL, tmp & ~offs); | 511 | mw32(MVS_CTL, tmp & ~offs); |
145 | } | 512 | } |
146 | 513 | ||
147 | tmp = mr32(INT_STAT_SRS) & (1U << *tfs); | 514 | tmp = mr32(MVS_INT_STAT_SRS_0) & (1U << *tfs); |
148 | if (tmp) | 515 | if (tmp) |
149 | mw32(INT_STAT_SRS, tmp); | 516 | mw32(MVS_INT_STAT_SRS_0, tmp); |
150 | 517 | ||
151 | *tfs = MVS_ID_NOT_MAPPED; | 518 | *tfs = MVS_ID_NOT_MAPPED; |
519 | return; | ||
152 | } | 520 | } |
153 | 521 | ||
154 | u8 mvs_assign_reg_set(struct mvs_info *mvi, struct mvs_port *port) | 522 | static u8 mvs_64xx_assign_reg_set(struct mvs_info *mvi, u8 *tfs) |
155 | { | 523 | { |
156 | int i; | 524 | int i; |
157 | u32 tmp, offs; | 525 | u32 tmp, offs; |
158 | void __iomem *regs = mvi->regs; | 526 | void __iomem *regs = mvi->regs; |
159 | 527 | ||
160 | if (port->taskfileset != MVS_ID_NOT_MAPPED) | 528 | if (*tfs != MVS_ID_NOT_MAPPED) |
161 | return 0; | 529 | return 0; |
162 | 530 | ||
163 | tmp = mr32(PCS); | 531 | tmp = mr32(MVS_PCS); |
164 | 532 | ||
165 | for (i = 0; i < mvi->chip->srs_sz; i++) { | 533 | for (i = 0; i < mvi->chip->srs_sz; i++) { |
166 | if (i == 16) | 534 | if (i == 16) |
167 | tmp = mr32(CTL); | 535 | tmp = mr32(MVS_CTL); |
168 | offs = 1U << ((i & 0x0f) + PCS_EN_SATA_REG_SHIFT); | 536 | offs = 1U << ((i & 0x0f) + PCS_EN_SATA_REG_SHIFT); |
169 | if (!(tmp & offs)) { | 537 | if (!(tmp & offs)) { |
170 | port->taskfileset = i; | 538 | *tfs = i; |
171 | 539 | ||
172 | if (i < 16) | 540 | if (i < 16) |
173 | mw32(PCS, tmp | offs); | 541 | mw32(MVS_PCS, tmp | offs); |
174 | else | 542 | else |
175 | mw32(CTL, tmp | offs); | 543 | mw32(MVS_CTL, tmp | offs); |
176 | tmp = mr32(INT_STAT_SRS) & (1U << i); | 544 | tmp = mr32(MVS_INT_STAT_SRS_0) & (1U << i); |
177 | if (tmp) | 545 | if (tmp) |
178 | mw32(INT_STAT_SRS, tmp); | 546 | mw32(MVS_INT_STAT_SRS_0, tmp); |
179 | return 0; | 547 | return 0; |
180 | } | 548 | } |
181 | } | 549 | } |
182 | return MVS_ID_NOT_MAPPED; | 550 | return MVS_ID_NOT_MAPPED; |
183 | } | 551 | } |
184 | 552 | ||
553 | void mvs_64xx_make_prd(struct scatterlist *scatter, int nr, void *prd) | ||
554 | { | ||
555 | int i; | ||
556 | struct scatterlist *sg; | ||
557 | struct mvs_prd *buf_prd = prd; | ||
558 | for_each_sg(scatter, sg, nr, i) { | ||
559 | buf_prd->addr = cpu_to_le64(sg_dma_address(sg)); | ||
560 | buf_prd->len = cpu_to_le32(sg_dma_len(sg)); | ||
561 | buf_prd++; | ||
562 | } | ||
563 | } | ||
564 | |||
565 | static int mvs_64xx_oob_done(struct mvs_info *mvi, int i) | ||
566 | { | ||
567 | u32 phy_st; | ||
568 | mvs_write_port_cfg_addr(mvi, i, | ||
569 | PHYR_PHY_STAT); | ||
570 | phy_st = mvs_read_port_cfg_data(mvi, i); | ||
571 | if (phy_st & PHY_OOB_DTCTD) | ||
572 | return 1; | ||
573 | return 0; | ||
574 | } | ||
575 | |||
576 | static void mvs_64xx_fix_phy_info(struct mvs_info *mvi, int i, | ||
577 | struct sas_identify_frame *id) | ||
578 | |||
579 | { | ||
580 | struct mvs_phy *phy = &mvi->phy[i]; | ||
581 | struct asd_sas_phy *sas_phy = &phy->sas_phy; | ||
582 | |||
583 | sas_phy->linkrate = | ||
584 | (phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >> | ||
585 | PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET; | ||
586 | |||
587 | phy->minimum_linkrate = | ||
588 | (phy->phy_status & | ||
589 | PHY_MIN_SPP_PHYS_LINK_RATE_MASK) >> 8; | ||
590 | phy->maximum_linkrate = | ||
591 | (phy->phy_status & | ||
592 | PHY_MAX_SPP_PHYS_LINK_RATE_MASK) >> 12; | ||
593 | |||
594 | mvs_write_port_cfg_addr(mvi, i, PHYR_IDENTIFY); | ||
595 | phy->dev_info = mvs_read_port_cfg_data(mvi, i); | ||
596 | |||
597 | mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_DEV_INFO); | ||
598 | phy->att_dev_info = mvs_read_port_cfg_data(mvi, i); | ||
599 | |||
600 | mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_ADDR_HI); | ||
601 | phy->att_dev_sas_addr = | ||
602 | (u64) mvs_read_port_cfg_data(mvi, i) << 32; | ||
603 | mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_ADDR_LO); | ||
604 | phy->att_dev_sas_addr |= mvs_read_port_cfg_data(mvi, i); | ||
605 | phy->att_dev_sas_addr = SAS_ADDR(&phy->att_dev_sas_addr); | ||
606 | } | ||
607 | |||
608 | static void mvs_64xx_phy_work_around(struct mvs_info *mvi, int i) | ||
609 | { | ||
610 | u32 tmp; | ||
611 | struct mvs_phy *phy = &mvi->phy[i]; | ||
612 | /* workaround for HW phy decoding error on 1.5g disk drive */ | ||
613 | mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE6); | ||
614 | tmp = mvs_read_port_vsr_data(mvi, i); | ||
615 | if (((phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >> | ||
616 | PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET) == | ||
617 | SAS_LINK_RATE_1_5_GBPS) | ||
618 | tmp &= ~PHY_MODE6_LATECLK; | ||
619 | else | ||
620 | tmp |= PHY_MODE6_LATECLK; | ||
621 | mvs_write_port_vsr_data(mvi, i, tmp); | ||
622 | } | ||
623 | |||
624 | void mvs_64xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id, | ||
625 | struct sas_phy_linkrates *rates) | ||
626 | { | ||
627 | u32 lrmin = 0, lrmax = 0; | ||
628 | u32 tmp; | ||
629 | |||
630 | tmp = mvs_read_phy_ctl(mvi, phy_id); | ||
631 | lrmin = (rates->minimum_linkrate << 8); | ||
632 | lrmax = (rates->maximum_linkrate << 12); | ||
633 | |||
634 | if (lrmin) { | ||
635 | tmp &= ~(0xf << 8); | ||
636 | tmp |= lrmin; | ||
637 | } | ||
638 | if (lrmax) { | ||
639 | tmp &= ~(0xf << 12); | ||
640 | tmp |= lrmax; | ||
641 | } | ||
642 | mvs_write_phy_ctl(mvi, phy_id, tmp); | ||
643 | mvs_64xx_phy_reset(mvi, phy_id, 1); | ||
644 | } | ||
645 | |||
646 | static void mvs_64xx_clear_active_cmds(struct mvs_info *mvi) | ||
647 | { | ||
648 | u32 tmp; | ||
649 | void __iomem *regs = mvi->regs; | ||
650 | tmp = mr32(MVS_PCS); | ||
651 | mw32(MVS_PCS, tmp & 0xFFFF); | ||
652 | mw32(MVS_PCS, tmp); | ||
653 | tmp = mr32(MVS_CTL); | ||
654 | mw32(MVS_CTL, tmp & 0xFFFF); | ||
655 | mw32(MVS_CTL, tmp); | ||
656 | } | ||
657 | |||
658 | |||
659 | u32 mvs_64xx_spi_read_data(struct mvs_info *mvi) | ||
660 | { | ||
661 | void __iomem *regs = mvi->regs_ex; | ||
662 | return ior32(SPI_DATA_REG_64XX); | ||
663 | } | ||
664 | |||
665 | void mvs_64xx_spi_write_data(struct mvs_info *mvi, u32 data) | ||
666 | { | ||
667 | void __iomem *regs = mvi->regs_ex; | ||
668 | iow32(SPI_DATA_REG_64XX, data); | ||
669 | } | ||
670 | |||
671 | |||
672 | int mvs_64xx_spi_buildcmd(struct mvs_info *mvi, | ||
673 | u32 *dwCmd, | ||
674 | u8 cmd, | ||
675 | u8 read, | ||
676 | u8 length, | ||
677 | u32 addr | ||
678 | ) | ||
679 | { | ||
680 | u32 dwTmp; | ||
681 | |||
682 | dwTmp = ((u32)cmd << 24) | ((u32)length << 19); | ||
683 | if (read) | ||
684 | dwTmp |= 1U<<23; | ||
685 | |||
686 | if (addr != MV_MAX_U32) { | ||
687 | dwTmp |= 1U<<22; | ||
688 | dwTmp |= (addr & 0x0003FFFF); | ||
689 | } | ||
690 | |||
691 | *dwCmd = dwTmp; | ||
692 | return 0; | ||
693 | } | ||
694 | |||
695 | |||
696 | int mvs_64xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd) | ||
697 | { | ||
698 | void __iomem *regs = mvi->regs_ex; | ||
699 | int retry; | ||
700 | |||
701 | for (retry = 0; retry < 1; retry++) { | ||
702 | iow32(SPI_CTRL_REG_64XX, SPI_CTRL_VENDOR_ENABLE); | ||
703 | iow32(SPI_CMD_REG_64XX, cmd); | ||
704 | iow32(SPI_CTRL_REG_64XX, | ||
705 | SPI_CTRL_VENDOR_ENABLE | SPI_CTRL_SPISTART); | ||
706 | } | ||
707 | |||
708 | return 0; | ||
709 | } | ||
710 | |||
711 | int mvs_64xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout) | ||
712 | { | ||
713 | void __iomem *regs = mvi->regs_ex; | ||
714 | u32 i, dwTmp; | ||
715 | |||
716 | for (i = 0; i < timeout; i++) { | ||
717 | dwTmp = ior32(SPI_CTRL_REG_64XX); | ||
718 | if (!(dwTmp & SPI_CTRL_SPISTART)) | ||
719 | return 0; | ||
720 | msleep(10); | ||
721 | } | ||
722 | |||
723 | return -1; | ||
724 | } | ||
725 | |||
726 | #ifndef DISABLE_HOTPLUG_DMA_FIX | ||
727 | void mvs_64xx_fix_dma(dma_addr_t buf_dma, int buf_len, int from, void *prd) | ||
728 | { | ||
729 | int i; | ||
730 | struct mvs_prd *buf_prd = prd; | ||
731 | buf_prd += from; | ||
732 | for (i = 0; i < MAX_SG_ENTRY - from; i++) { | ||
733 | buf_prd->addr = cpu_to_le64(buf_dma); | ||
734 | buf_prd->len = cpu_to_le32(buf_len); | ||
735 | ++buf_prd; | ||
736 | } | ||
737 | } | ||
738 | #endif | ||
739 | |||
740 | const struct mvs_dispatch mvs_64xx_dispatch = { | ||
741 | "mv64xx", | ||
742 | mvs_64xx_init, | ||
743 | NULL, | ||
744 | mvs_64xx_ioremap, | ||
745 | mvs_64xx_iounmap, | ||
746 | mvs_64xx_isr, | ||
747 | mvs_64xx_isr_status, | ||
748 | mvs_64xx_interrupt_enable, | ||
749 | mvs_64xx_interrupt_disable, | ||
750 | mvs_read_phy_ctl, | ||
751 | mvs_write_phy_ctl, | ||
752 | mvs_read_port_cfg_data, | ||
753 | mvs_write_port_cfg_data, | ||
754 | mvs_write_port_cfg_addr, | ||
755 | mvs_read_port_vsr_data, | ||
756 | mvs_write_port_vsr_data, | ||
757 | mvs_write_port_vsr_addr, | ||
758 | mvs_read_port_irq_stat, | ||
759 | mvs_write_port_irq_stat, | ||
760 | mvs_read_port_irq_mask, | ||
761 | mvs_write_port_irq_mask, | ||
762 | mvs_get_sas_addr, | ||
763 | mvs_64xx_command_active, | ||
764 | mvs_64xx_issue_stop, | ||
765 | mvs_start_delivery, | ||
766 | mvs_rx_update, | ||
767 | mvs_int_full, | ||
768 | mvs_64xx_assign_reg_set, | ||
769 | mvs_64xx_free_reg_set, | ||
770 | mvs_get_prd_size, | ||
771 | mvs_get_prd_count, | ||
772 | mvs_64xx_make_prd, | ||
773 | mvs_64xx_detect_porttype, | ||
774 | mvs_64xx_oob_done, | ||
775 | mvs_64xx_fix_phy_info, | ||
776 | mvs_64xx_phy_work_around, | ||
777 | mvs_64xx_phy_set_link_rate, | ||
778 | mvs_hw_max_link_rate, | ||
779 | mvs_64xx_phy_disable, | ||
780 | mvs_64xx_phy_enable, | ||
781 | mvs_64xx_phy_reset, | ||
782 | mvs_64xx_stp_reset, | ||
783 | mvs_64xx_clear_active_cmds, | ||
784 | mvs_64xx_spi_read_data, | ||
785 | mvs_64xx_spi_write_data, | ||
786 | mvs_64xx_spi_buildcmd, | ||
787 | mvs_64xx_spi_issuecmd, | ||
788 | mvs_64xx_spi_waitdataready, | ||
789 | #ifndef DISABLE_HOTPLUG_DMA_FIX | ||
790 | mvs_64xx_fix_dma, | ||
791 | #endif | ||
792 | }; | ||
793 | |||