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