]>
Commit | Line | Data |
---|---|---|
dd4969a8 | 1 | /* |
20b09c29 AY |
2 | * Marvell 88SE64xx hardware specific |
3 | * | |
4 | * Copyright 2007 Red Hat, Inc. | |
5 | * Copyright 2008 Marvell. <kewei@marvell.com> | |
6 | * | |
7 | * This file is licensed under GPLv2. | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU General Public License as | |
11 | * published by the Free Software Foundation; version 2 of the | |
12 | * License. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
22 | * USA | |
23 | */ | |
dd4969a8 JG |
24 | |
25 | #include "mv_sas.h" | |
26 | #include "mv_64xx.h" | |
27 | #include "mv_chips.h" | |
28 | ||
20b09c29 | 29 | static void mvs_64xx_detect_porttype(struct mvs_info *mvi, int i) |
dd4969a8 JG |
30 | { |
31 | void __iomem *regs = mvi->regs; | |
32 | u32 reg; | |
33 | struct mvs_phy *phy = &mvi->phy[i]; | |
34 | ||
35 | /* TODO check & save device type */ | |
20b09c29 AY |
36 | reg = mr32(MVS_GBL_PORT_TYPE); |
37 | phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA); | |
dd4969a8 JG |
38 | if (reg & MODE_SAS_SATA & (1 << i)) |
39 | phy->phy_type |= PORT_TYPE_SAS; | |
40 | else | |
41 | phy->phy_type |= PORT_TYPE_SATA; | |
42 | } | |
43 | ||
20b09c29 | 44 | static void __devinit mvs_64xx_enable_xmt(struct mvs_info *mvi, int phy_id) |
dd4969a8 JG |
45 | { |
46 | void __iomem *regs = mvi->regs; | |
47 | u32 tmp; | |
48 | ||
20b09c29 | 49 | tmp = mr32(MVS_PCS); |
dd4969a8 | 50 | if (mvi->chip->n_phy <= 4) |
20b09c29 AY |
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; | |
dd4969a8 | 108 | else |
20b09c29 AY |
109 | tmp |= (1U << (phy_id - 4)) << PCTL_LINK_OFFS; |
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; | |
dd4969a8 JG |
195 | } |
196 | ||
20b09c29 | 197 | static void mvs_64xx_phy_disable(struct mvs_info *mvi, u32 phy_id) |
dd4969a8 JG |
198 | { |
199 | void __iomem *regs = mvi->regs; | |
200 | u32 tmp; | |
20b09c29 AY |
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 | } | |
dd4969a8 | 240 | |
20b09c29 AY |
241 | static int __devinit mvs_64xx_init(struct mvs_info *mvi) |
242 | { | |
243 | void __iomem *regs = mvi->regs; | |
244 | int i; | |
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 | } | |
dd4969a8 | 260 | |
20b09c29 AY |
261 | /* Init Chip */ |
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 | } | |
dd4969a8 | 297 | |
20b09c29 AY |
298 | /* reset control */ |
299 | mw32(MVS_PCS, 0); /* MVS_PCS */ | |
300 | /* init phys */ | |
301 | mvs_64xx_phy_hacks(mvi); | |
dd4969a8 | 302 | |
20b09c29 AY |
303 | /* enable auto port detection */ |
304 | mw32(MVS_GBL_PORT_TYPE, MODE_AUTO_DET_EN); | |
dd4969a8 | 305 | |
20b09c29 AY |
306 | mw32(MVS_CMD_LIST_LO, mvi->slot_dma); |
307 | mw32(MVS_CMD_LIST_HI, (mvi->slot_dma >> 16) >> 16); | |
dd4969a8 | 308 | |
20b09c29 AY |
309 | mw32(MVS_RX_FIS_LO, mvi->rx_fis_dma); |
310 | mw32(MVS_RX_FIS_HI, (mvi->rx_fis_dma >> 16) >> 16); | |
dd4969a8 | 311 | |
20b09c29 AY |
312 | mw32(MVS_TX_CFG, MVS_CHIP_SLOT_SZ); |
313 | mw32(MVS_TX_LO, mvi->tx_dma); | |
314 | mw32(MVS_TX_HI, (mvi->tx_dma >> 16) >> 16); | |
dd4969a8 | 315 | |
20b09c29 AY |
316 | mw32(MVS_RX_CFG, MVS_RX_RING_SZ); |
317 | mw32(MVS_RX_LO, mvi->rx_dma); | |
318 | mw32(MVS_RX_HI, (mvi->rx_dma >> 16) >> 16); | |
dd4969a8 | 319 | |
20b09c29 AY |
320 | for (i = 0; i < mvi->chip->n_phy; i++) { |
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)); | |
dd4969a8 | 325 | |
20b09c29 | 326 | mvs_64xx_enable_xmt(mvi, i); |
dd4969a8 | 327 | |
20b09c29 AY |
328 | mvs_64xx_phy_reset(mvi, i, 1); |
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 | } | |
dd4969a8 | 358 | |
20b09c29 AY |
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; | |
dd4969a8 JG |
405 | } |
406 | ||
20b09c29 AY |
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) | |
dd4969a8 JG |
421 | { |
422 | void __iomem *regs = mvi->regs; | |
423 | u32 tmp; | |
424 | ||
20b09c29 AY |
425 | tmp = mr32(MVS_GBL_CTL); |
426 | mw32(MVS_GBL_CTL, tmp | INT_EN); | |
427 | } | |
dd4969a8 | 428 | |
20b09c29 AY |
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); | |
dd4969a8 JG |
436 | } |
437 | ||
20b09c29 | 438 | static u32 mvs_64xx_isr_status(struct mvs_info *mvi, int irq) |
dd4969a8 JG |
439 | { |
440 | void __iomem *regs = mvi->regs; | |
20b09c29 AY |
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 | { | |
dd4969a8 | 471 | u32 tmp; |
20b09c29 AY |
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 | } | |
dd4969a8 | 481 | |
20b09c29 AY |
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; | |
dd4969a8 | 487 | |
20b09c29 AY |
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); | |
dd4969a8 JG |
495 | } |
496 | ||
20b09c29 | 497 | static void mvs_64xx_free_reg_set(struct mvs_info *mvi, u8 *tfs) |
dd4969a8 JG |
498 | { |
499 | void __iomem *regs = mvi->regs; | |
500 | u32 tmp, offs; | |
dd4969a8 JG |
501 | |
502 | if (*tfs == MVS_ID_NOT_MAPPED) | |
503 | return; | |
504 | ||
505 | offs = 1U << ((*tfs & 0x0f) + PCS_EN_SATA_REG_SHIFT); | |
506 | if (*tfs < 16) { | |
20b09c29 AY |
507 | tmp = mr32(MVS_PCS); |
508 | mw32(MVS_PCS, tmp & ~offs); | |
dd4969a8 | 509 | } else { |
20b09c29 AY |
510 | tmp = mr32(MVS_CTL); |
511 | mw32(MVS_CTL, tmp & ~offs); | |
dd4969a8 JG |
512 | } |
513 | ||
20b09c29 | 514 | tmp = mr32(MVS_INT_STAT_SRS_0) & (1U << *tfs); |
dd4969a8 | 515 | if (tmp) |
20b09c29 | 516 | mw32(MVS_INT_STAT_SRS_0, tmp); |
dd4969a8 JG |
517 | |
518 | *tfs = MVS_ID_NOT_MAPPED; | |
20b09c29 | 519 | return; |
dd4969a8 JG |
520 | } |
521 | ||
20b09c29 | 522 | static u8 mvs_64xx_assign_reg_set(struct mvs_info *mvi, u8 *tfs) |
dd4969a8 JG |
523 | { |
524 | int i; | |
525 | u32 tmp, offs; | |
526 | void __iomem *regs = mvi->regs; | |
527 | ||
20b09c29 | 528 | if (*tfs != MVS_ID_NOT_MAPPED) |
dd4969a8 JG |
529 | return 0; |
530 | ||
20b09c29 | 531 | tmp = mr32(MVS_PCS); |
dd4969a8 JG |
532 | |
533 | for (i = 0; i < mvi->chip->srs_sz; i++) { | |
534 | if (i == 16) | |
20b09c29 | 535 | tmp = mr32(MVS_CTL); |
dd4969a8 JG |
536 | offs = 1U << ((i & 0x0f) + PCS_EN_SATA_REG_SHIFT); |
537 | if (!(tmp & offs)) { | |
20b09c29 | 538 | *tfs = i; |
dd4969a8 JG |
539 | |
540 | if (i < 16) | |
20b09c29 | 541 | mw32(MVS_PCS, tmp | offs); |
dd4969a8 | 542 | else |
20b09c29 AY |
543 | mw32(MVS_CTL, tmp | offs); |
544 | tmp = mr32(MVS_INT_STAT_SRS_0) & (1U << i); | |
dd4969a8 | 545 | if (tmp) |
20b09c29 | 546 | mw32(MVS_INT_STAT_SRS_0, tmp); |
dd4969a8 JG |
547 | return 0; |
548 | } | |
549 | } | |
550 | return MVS_ID_NOT_MAPPED; | |
551 | } | |
552 | ||
20b09c29 AY |
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 |