1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2005-2014 Brocade Communications Systems, Inc. |
4 | * Copyright (c) 2014- QLogic Corporation. |
5 | * All rights reserved |
6 | * www.qlogic.com |
7 | * |
8 | * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter. |
9 | */ |
10 | |
11 | #include "bfad_drv.h" |
12 | #include "bfa_ioc.h" |
13 | #include "bfi_reg.h" |
14 | #include "bfa_defs.h" |
15 | |
16 | BFA_TRC_FILE(CNA, IOC_CB); |
17 | |
18 | #define bfa_ioc_cb_join_pos(__ioc) ((u32) (1 << BFA_IOC_CB_JOIN_SH)) |
19 | |
20 | /* |
21 | * forward declarations |
22 | */ |
23 | static bfa_boolean_t bfa_ioc_cb_firmware_lock(struct bfa_ioc_s *ioc); |
24 | static void bfa_ioc_cb_firmware_unlock(struct bfa_ioc_s *ioc); |
25 | static void bfa_ioc_cb_reg_init(struct bfa_ioc_s *ioc); |
26 | static void bfa_ioc_cb_map_port(struct bfa_ioc_s *ioc); |
27 | static void bfa_ioc_cb_isr_mode_set(struct bfa_ioc_s *ioc, bfa_boolean_t msix); |
28 | static void bfa_ioc_cb_notify_fail(struct bfa_ioc_s *ioc); |
29 | static void bfa_ioc_cb_ownership_reset(struct bfa_ioc_s *ioc); |
30 | static bfa_boolean_t bfa_ioc_cb_sync_start(struct bfa_ioc_s *ioc); |
31 | static void bfa_ioc_cb_sync_join(struct bfa_ioc_s *ioc); |
32 | static void bfa_ioc_cb_sync_leave(struct bfa_ioc_s *ioc); |
33 | static void bfa_ioc_cb_sync_ack(struct bfa_ioc_s *ioc); |
34 | static bfa_boolean_t bfa_ioc_cb_sync_complete(struct bfa_ioc_s *ioc); |
35 | static void bfa_ioc_cb_set_cur_ioc_fwstate( |
36 | struct bfa_ioc_s *ioc, enum bfi_ioc_state fwstate); |
37 | static enum bfi_ioc_state bfa_ioc_cb_get_cur_ioc_fwstate(struct bfa_ioc_s *ioc); |
38 | static void bfa_ioc_cb_set_alt_ioc_fwstate( |
39 | struct bfa_ioc_s *ioc, enum bfi_ioc_state fwstate); |
40 | static enum bfi_ioc_state bfa_ioc_cb_get_alt_ioc_fwstate(struct bfa_ioc_s *ioc); |
41 | |
42 | static struct bfa_ioc_hwif_s hwif_cb; |
43 | |
44 | /* |
45 | * Called from bfa_ioc_attach() to map asic specific calls. |
46 | */ |
47 | void |
48 | bfa_ioc_set_cb_hwif(struct bfa_ioc_s *ioc) |
49 | { |
50 | hwif_cb.ioc_pll_init = bfa_ioc_cb_pll_init; |
51 | hwif_cb.ioc_firmware_lock = bfa_ioc_cb_firmware_lock; |
52 | hwif_cb.ioc_firmware_unlock = bfa_ioc_cb_firmware_unlock; |
53 | hwif_cb.ioc_reg_init = bfa_ioc_cb_reg_init; |
54 | hwif_cb.ioc_map_port = bfa_ioc_cb_map_port; |
55 | hwif_cb.ioc_isr_mode_set = bfa_ioc_cb_isr_mode_set; |
56 | hwif_cb.ioc_notify_fail = bfa_ioc_cb_notify_fail; |
57 | hwif_cb.ioc_ownership_reset = bfa_ioc_cb_ownership_reset; |
58 | hwif_cb.ioc_sync_start = bfa_ioc_cb_sync_start; |
59 | hwif_cb.ioc_sync_join = bfa_ioc_cb_sync_join; |
60 | hwif_cb.ioc_sync_leave = bfa_ioc_cb_sync_leave; |
61 | hwif_cb.ioc_sync_ack = bfa_ioc_cb_sync_ack; |
62 | hwif_cb.ioc_sync_complete = bfa_ioc_cb_sync_complete; |
63 | hwif_cb.ioc_set_fwstate = bfa_ioc_cb_set_cur_ioc_fwstate; |
64 | hwif_cb.ioc_get_fwstate = bfa_ioc_cb_get_cur_ioc_fwstate; |
65 | hwif_cb.ioc_set_alt_fwstate = bfa_ioc_cb_set_alt_ioc_fwstate; |
66 | hwif_cb.ioc_get_alt_fwstate = bfa_ioc_cb_get_alt_ioc_fwstate; |
67 | |
68 | ioc->ioc_hwif = &hwif_cb; |
69 | } |
70 | |
71 | /* |
72 | * Return true if firmware of current driver matches the running firmware. |
73 | */ |
74 | static bfa_boolean_t |
75 | bfa_ioc_cb_firmware_lock(struct bfa_ioc_s *ioc) |
76 | { |
77 | enum bfi_ioc_state alt_fwstate, cur_fwstate; |
78 | struct bfi_ioc_image_hdr_s fwhdr; |
79 | |
80 | cur_fwstate = bfa_ioc_cb_get_cur_ioc_fwstate(ioc); |
81 | bfa_trc(ioc, cur_fwstate); |
82 | alt_fwstate = bfa_ioc_cb_get_alt_ioc_fwstate(ioc); |
83 | bfa_trc(ioc, alt_fwstate); |
84 | |
85 | /* |
86 | * Uninit implies this is the only driver as of now. |
87 | */ |
88 | if (cur_fwstate == BFI_IOC_UNINIT) |
89 | return BFA_TRUE; |
90 | /* |
91 | * Check if another driver with a different firmware is active |
92 | */ |
93 | bfa_ioc_fwver_get(ioc, fwhdr: &fwhdr); |
94 | if (!bfa_ioc_fwver_cmp(ioc, fwhdr: &fwhdr) && |
95 | alt_fwstate != BFI_IOC_DISABLED) { |
96 | bfa_trc(ioc, alt_fwstate); |
97 | return BFA_FALSE; |
98 | } |
99 | |
100 | return BFA_TRUE; |
101 | } |
102 | |
103 | static void |
104 | bfa_ioc_cb_firmware_unlock(struct bfa_ioc_s *ioc) |
105 | { |
106 | } |
107 | |
108 | /* |
109 | * Notify other functions on HB failure. |
110 | */ |
111 | static void |
112 | bfa_ioc_cb_notify_fail(struct bfa_ioc_s *ioc) |
113 | { |
114 | writel(val: ~0U, addr: ioc->ioc_regs.err_set); |
115 | readl(addr: ioc->ioc_regs.err_set); |
116 | } |
117 | |
118 | /* |
119 | * Host to LPU mailbox message addresses |
120 | */ |
121 | static struct { u32 hfn_mbox, lpu_mbox, hfn_pgn; } iocreg_fnreg[] = { |
122 | { HOSTFN0_LPU_MBOX0_0, LPU_HOSTFN0_MBOX0_0, HOST_PAGE_NUM_FN0 }, |
123 | { HOSTFN1_LPU_MBOX0_8, LPU_HOSTFN1_MBOX0_8, HOST_PAGE_NUM_FN1 } |
124 | }; |
125 | |
126 | /* |
127 | * Host <-> LPU mailbox command/status registers |
128 | */ |
129 | static struct { u32 hfn, lpu; } iocreg_mbcmd[] = { |
130 | |
131 | { HOSTFN0_LPU0_CMD_STAT, LPU0_HOSTFN0_CMD_STAT }, |
132 | { HOSTFN1_LPU1_CMD_STAT, LPU1_HOSTFN1_CMD_STAT } |
133 | }; |
134 | |
135 | static void |
136 | bfa_ioc_cb_reg_init(struct bfa_ioc_s *ioc) |
137 | { |
138 | void __iomem *rb; |
139 | int pcifn = bfa_ioc_pcifn(ioc); |
140 | |
141 | rb = bfa_ioc_bar0(ioc); |
142 | |
143 | ioc->ioc_regs.hfn_mbox = rb + iocreg_fnreg[pcifn].hfn_mbox; |
144 | ioc->ioc_regs.lpu_mbox = rb + iocreg_fnreg[pcifn].lpu_mbox; |
145 | ioc->ioc_regs.host_page_num_fn = rb + iocreg_fnreg[pcifn].hfn_pgn; |
146 | |
147 | if (ioc->port_id == 0) { |
148 | ioc->ioc_regs.heartbeat = rb + BFA_IOC0_HBEAT_REG; |
149 | ioc->ioc_regs.ioc_fwstate = rb + BFA_IOC0_STATE_REG; |
150 | ioc->ioc_regs.alt_ioc_fwstate = rb + BFA_IOC1_STATE_REG; |
151 | } else { |
152 | ioc->ioc_regs.heartbeat = (rb + BFA_IOC1_HBEAT_REG); |
153 | ioc->ioc_regs.ioc_fwstate = (rb + BFA_IOC1_STATE_REG); |
154 | ioc->ioc_regs.alt_ioc_fwstate = (rb + BFA_IOC0_STATE_REG); |
155 | } |
156 | |
157 | /* |
158 | * Host <-> LPU mailbox command/status registers |
159 | */ |
160 | ioc->ioc_regs.hfn_mbox_cmd = rb + iocreg_mbcmd[pcifn].hfn; |
161 | ioc->ioc_regs.lpu_mbox_cmd = rb + iocreg_mbcmd[pcifn].lpu; |
162 | |
163 | /* |
164 | * PSS control registers |
165 | */ |
166 | ioc->ioc_regs.pss_ctl_reg = (rb + PSS_CTL_REG); |
167 | ioc->ioc_regs.pss_err_status_reg = (rb + PSS_ERR_STATUS_REG); |
168 | ioc->ioc_regs.app_pll_fast_ctl_reg = (rb + APP_PLL_LCLK_CTL_REG); |
169 | ioc->ioc_regs.app_pll_slow_ctl_reg = (rb + APP_PLL_SCLK_CTL_REG); |
170 | |
171 | /* |
172 | * IOC semaphore registers and serialization |
173 | */ |
174 | ioc->ioc_regs.ioc_sem_reg = (rb + HOST_SEM0_REG); |
175 | ioc->ioc_regs.ioc_init_sem_reg = (rb + HOST_SEM2_REG); |
176 | |
177 | /* |
178 | * sram memory access |
179 | */ |
180 | ioc->ioc_regs.smem_page_start = (rb + PSS_SMEM_PAGE_START); |
181 | ioc->ioc_regs.smem_pg0 = BFI_IOC_SMEM_PG0_CB; |
182 | |
183 | /* |
184 | * err set reg : for notification of hb failure |
185 | */ |
186 | ioc->ioc_regs.err_set = (rb + ERR_SET_REG); |
187 | } |
188 | |
189 | /* |
190 | * Initialize IOC to port mapping. |
191 | */ |
192 | |
193 | static void |
194 | bfa_ioc_cb_map_port(struct bfa_ioc_s *ioc) |
195 | { |
196 | /* |
197 | * For crossbow, port id is same as pci function. |
198 | */ |
199 | ioc->port_id = bfa_ioc_pcifn(ioc); |
200 | |
201 | bfa_trc(ioc, ioc->port_id); |
202 | } |
203 | |
204 | /* |
205 | * Set interrupt mode for a function: INTX or MSIX |
206 | */ |
207 | static void |
208 | bfa_ioc_cb_isr_mode_set(struct bfa_ioc_s *ioc, bfa_boolean_t msix) |
209 | { |
210 | } |
211 | |
212 | /* |
213 | * Synchronized IOC failure processing routines |
214 | */ |
215 | static bfa_boolean_t |
216 | bfa_ioc_cb_sync_start(struct bfa_ioc_s *ioc) |
217 | { |
218 | u32 ioc_fwstate = readl(addr: ioc->ioc_regs.ioc_fwstate); |
219 | |
220 | /** |
221 | * Driver load time. If the join bit is set, |
222 | * it is due to an unclean exit by the driver for this |
223 | * PCI fn in the previous incarnation. Whoever comes here first |
224 | * should clean it up, no matter which PCI fn. |
225 | */ |
226 | if (ioc_fwstate & BFA_IOC_CB_JOIN_MASK) { |
227 | writel(val: BFI_IOC_UNINIT, addr: ioc->ioc_regs.ioc_fwstate); |
228 | writel(val: BFI_IOC_UNINIT, addr: ioc->ioc_regs.alt_ioc_fwstate); |
229 | return BFA_TRUE; |
230 | } |
231 | |
232 | return bfa_ioc_cb_sync_complete(ioc); |
233 | } |
234 | |
235 | /* |
236 | * Cleanup hw semaphore and usecnt registers |
237 | */ |
238 | static void |
239 | bfa_ioc_cb_ownership_reset(struct bfa_ioc_s *ioc) |
240 | { |
241 | |
242 | /* |
243 | * Read the hw sem reg to make sure that it is locked |
244 | * before we clear it. If it is not locked, writing 1 |
245 | * will lock it instead of clearing it. |
246 | */ |
247 | readl(addr: ioc->ioc_regs.ioc_sem_reg); |
248 | writel(val: 1, addr: ioc->ioc_regs.ioc_sem_reg); |
249 | } |
250 | |
251 | /* |
252 | * Synchronized IOC failure processing routines |
253 | */ |
254 | static void |
255 | bfa_ioc_cb_sync_join(struct bfa_ioc_s *ioc) |
256 | { |
257 | u32 r32 = readl(addr: ioc->ioc_regs.ioc_fwstate); |
258 | u32 join_pos = bfa_ioc_cb_join_pos(ioc); |
259 | |
260 | writel(val: (r32 | join_pos), addr: ioc->ioc_regs.ioc_fwstate); |
261 | } |
262 | |
263 | static void |
264 | bfa_ioc_cb_sync_leave(struct bfa_ioc_s *ioc) |
265 | { |
266 | u32 r32 = readl(addr: ioc->ioc_regs.ioc_fwstate); |
267 | u32 join_pos = bfa_ioc_cb_join_pos(ioc); |
268 | |
269 | writel(val: (r32 & ~join_pos), addr: ioc->ioc_regs.ioc_fwstate); |
270 | } |
271 | |
272 | static void |
273 | bfa_ioc_cb_set_cur_ioc_fwstate(struct bfa_ioc_s *ioc, |
274 | enum bfi_ioc_state fwstate) |
275 | { |
276 | u32 r32 = readl(addr: ioc->ioc_regs.ioc_fwstate); |
277 | |
278 | writel(val: (fwstate | (r32 & BFA_IOC_CB_JOIN_MASK)), |
279 | addr: ioc->ioc_regs.ioc_fwstate); |
280 | } |
281 | |
282 | static enum bfi_ioc_state |
283 | bfa_ioc_cb_get_cur_ioc_fwstate(struct bfa_ioc_s *ioc) |
284 | { |
285 | return (enum bfi_ioc_state)(readl(addr: ioc->ioc_regs.ioc_fwstate) & |
286 | BFA_IOC_CB_FWSTATE_MASK); |
287 | } |
288 | |
289 | static void |
290 | bfa_ioc_cb_set_alt_ioc_fwstate(struct bfa_ioc_s *ioc, |
291 | enum bfi_ioc_state fwstate) |
292 | { |
293 | u32 r32 = readl(addr: ioc->ioc_regs.alt_ioc_fwstate); |
294 | |
295 | writel(val: (fwstate | (r32 & BFA_IOC_CB_JOIN_MASK)), |
296 | addr: ioc->ioc_regs.alt_ioc_fwstate); |
297 | } |
298 | |
299 | static enum bfi_ioc_state |
300 | bfa_ioc_cb_get_alt_ioc_fwstate(struct bfa_ioc_s *ioc) |
301 | { |
302 | return (enum bfi_ioc_state)(readl(addr: ioc->ioc_regs.alt_ioc_fwstate) & |
303 | BFA_IOC_CB_FWSTATE_MASK); |
304 | } |
305 | |
306 | static void |
307 | bfa_ioc_cb_sync_ack(struct bfa_ioc_s *ioc) |
308 | { |
309 | bfa_ioc_cb_set_cur_ioc_fwstate(ioc, fwstate: BFI_IOC_FAIL); |
310 | } |
311 | |
312 | static bfa_boolean_t |
313 | bfa_ioc_cb_sync_complete(struct bfa_ioc_s *ioc) |
314 | { |
315 | u32 fwstate, alt_fwstate; |
316 | fwstate = bfa_ioc_cb_get_cur_ioc_fwstate(ioc); |
317 | |
318 | /* |
319 | * At this point, this IOC is hoding the hw sem in the |
320 | * start path (fwcheck) OR in the disable/enable path |
321 | * OR to check if the other IOC has acknowledged failure. |
322 | * |
323 | * So, this IOC can be in UNINIT, INITING, DISABLED, FAIL |
324 | * or in MEMTEST states. In a normal scenario, this IOC |
325 | * can not be in OP state when this function is called. |
326 | * |
327 | * However, this IOC could still be in OP state when |
328 | * the OS driver is starting up, if the OptROM code has |
329 | * left it in that state. |
330 | * |
331 | * If we had marked this IOC's fwstate as BFI_IOC_FAIL |
332 | * in the failure case and now, if the fwstate is not |
333 | * BFI_IOC_FAIL it implies that the other PCI fn have |
334 | * reinitialized the ASIC or this IOC got disabled, so |
335 | * return TRUE. |
336 | */ |
337 | if (fwstate == BFI_IOC_UNINIT || |
338 | fwstate == BFI_IOC_INITING || |
339 | fwstate == BFI_IOC_DISABLED || |
340 | fwstate == BFI_IOC_MEMTEST || |
341 | fwstate == BFI_IOC_OP) |
342 | return BFA_TRUE; |
343 | else { |
344 | alt_fwstate = bfa_ioc_cb_get_alt_ioc_fwstate(ioc); |
345 | if (alt_fwstate == BFI_IOC_FAIL || |
346 | alt_fwstate == BFI_IOC_DISABLED || |
347 | alt_fwstate == BFI_IOC_UNINIT || |
348 | alt_fwstate == BFI_IOC_INITING || |
349 | alt_fwstate == BFI_IOC_MEMTEST) |
350 | return BFA_TRUE; |
351 | else |
352 | return BFA_FALSE; |
353 | } |
354 | } |
355 | |
356 | bfa_status_t |
357 | bfa_ioc_cb_pll_init(void __iomem *rb, enum bfi_asic_mode fcmode) |
358 | { |
359 | u32 pll_sclk, pll_fclk, join_bits; |
360 | |
361 | pll_sclk = __APP_PLL_SCLK_ENABLE | __APP_PLL_SCLK_LRESETN | |
362 | __APP_PLL_SCLK_P0_1(3U) | |
363 | __APP_PLL_SCLK_JITLMT0_1(3U) | |
364 | __APP_PLL_SCLK_CNTLMT0_1(3U); |
365 | pll_fclk = __APP_PLL_LCLK_ENABLE | __APP_PLL_LCLK_LRESETN | |
366 | __APP_PLL_LCLK_RSEL200500 | __APP_PLL_LCLK_P0_1(3U) | |
367 | __APP_PLL_LCLK_JITLMT0_1(3U) | |
368 | __APP_PLL_LCLK_CNTLMT0_1(3U); |
369 | join_bits = readl(addr: rb + BFA_IOC0_STATE_REG) & |
370 | BFA_IOC_CB_JOIN_MASK; |
371 | writel(val: (BFI_IOC_UNINIT | join_bits), addr: (rb + BFA_IOC0_STATE_REG)); |
372 | join_bits = readl(addr: rb + BFA_IOC1_STATE_REG) & |
373 | BFA_IOC_CB_JOIN_MASK; |
374 | writel(val: (BFI_IOC_UNINIT | join_bits), addr: (rb + BFA_IOC1_STATE_REG)); |
375 | writel(val: 0xffffffffU, addr: (rb + HOSTFN0_INT_MSK)); |
376 | writel(val: 0xffffffffU, addr: (rb + HOSTFN1_INT_MSK)); |
377 | writel(val: 0xffffffffU, addr: (rb + HOSTFN0_INT_STATUS)); |
378 | writel(val: 0xffffffffU, addr: (rb + HOSTFN1_INT_STATUS)); |
379 | writel(val: 0xffffffffU, addr: (rb + HOSTFN0_INT_MSK)); |
380 | writel(val: 0xffffffffU, addr: (rb + HOSTFN1_INT_MSK)); |
381 | writel(__APP_PLL_SCLK_LOGIC_SOFT_RESET, addr: rb + APP_PLL_SCLK_CTL_REG); |
382 | writel(__APP_PLL_SCLK_BYPASS | __APP_PLL_SCLK_LOGIC_SOFT_RESET, |
383 | addr: rb + APP_PLL_SCLK_CTL_REG); |
384 | writel(__APP_PLL_LCLK_LOGIC_SOFT_RESET, addr: rb + APP_PLL_LCLK_CTL_REG); |
385 | writel(__APP_PLL_LCLK_BYPASS | __APP_PLL_LCLK_LOGIC_SOFT_RESET, |
386 | addr: rb + APP_PLL_LCLK_CTL_REG); |
387 | udelay(2); |
388 | writel(__APP_PLL_SCLK_LOGIC_SOFT_RESET, addr: rb + APP_PLL_SCLK_CTL_REG); |
389 | writel(__APP_PLL_LCLK_LOGIC_SOFT_RESET, addr: rb + APP_PLL_LCLK_CTL_REG); |
390 | writel(val: pll_sclk | __APP_PLL_SCLK_LOGIC_SOFT_RESET, |
391 | addr: rb + APP_PLL_SCLK_CTL_REG); |
392 | writel(val: pll_fclk | __APP_PLL_LCLK_LOGIC_SOFT_RESET, |
393 | addr: rb + APP_PLL_LCLK_CTL_REG); |
394 | udelay(2000); |
395 | writel(val: 0xffffffffU, addr: (rb + HOSTFN0_INT_STATUS)); |
396 | writel(val: 0xffffffffU, addr: (rb + HOSTFN1_INT_STATUS)); |
397 | writel(val: pll_sclk, addr: (rb + APP_PLL_SCLK_CTL_REG)); |
398 | writel(val: pll_fclk, addr: (rb + APP_PLL_LCLK_CTL_REG)); |
399 | |
400 | return BFA_STATUS_OK; |
401 | } |
402 | |