Commit | Line | Data |
---|---|---|
a83369b6 FL |
1 | /* |
2 | * Copyright (c) 2011 Broadcom Corporation | |
3 | * | |
4 | * Permission to use, copy, modify, and/or distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | |
11 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | |
13 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |
14 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | */ | |
16 | /* ***** SDIO interface chip backplane handle functions ***** */ | |
17 | ||
18 | #include <linux/types.h> | |
19 | #include <linux/netdevice.h> | |
20 | #include <linux/mmc/card.h> | |
fe040158 | 21 | #include <linux/mmc/sdio_func.h> |
61213be4 | 22 | #include <linux/ssb/ssb_regs.h> |
99ba15cd | 23 | #include <linux/bcma/bcma.h> |
61213be4 | 24 | |
a83369b6 FL |
25 | #include <chipcommon.h> |
26 | #include <brcm_hw_ids.h> | |
27 | #include <brcmu_wifi.h> | |
28 | #include <brcmu_utils.h> | |
2d4a9af1 | 29 | #include <soc.h> |
a83369b6 FL |
30 | #include "dhd_dbg.h" |
31 | #include "sdio_host.h" | |
32 | #include "sdio_chip.h" | |
33 | ||
34 | /* chip core base & ramsize */ | |
35 | /* bcm4329 */ | |
36 | /* SDIO device core, ID 0x829 */ | |
37 | #define BCM4329_CORE_BUS_BASE 0x18011000 | |
38 | /* internal memory core, ID 0x80e */ | |
39 | #define BCM4329_CORE_SOCRAM_BASE 0x18003000 | |
40 | /* ARM Cortex M3 core, ID 0x82a */ | |
41 | #define BCM4329_CORE_ARM_BASE 0x18002000 | |
42 | #define BCM4329_RAMSIZE 0x48000 | |
43 | ||
369508c5 HM |
44 | /* bcm43143 */ |
45 | /* SDIO device core */ | |
46 | #define BCM43143_CORE_BUS_BASE 0x18002000 | |
47 | /* internal memory core */ | |
48 | #define BCM43143_CORE_SOCRAM_BASE 0x18004000 | |
49 | /* ARM Cortex M3 core, ID 0x82a */ | |
50 | #define BCM43143_CORE_ARM_BASE 0x18003000 | |
51 | #define BCM43143_RAMSIZE 0x70000 | |
52 | ||
a83369b6 | 53 | #define SBCOREREV(sbidh) \ |
61213be4 FL |
54 | ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \ |
55 | ((sbidh) & SSB_IDHIGH_RCLO)) | |
a83369b6 | 56 | |
6ca687d9 FL |
57 | /* SOC Interconnect types (aka chip types) */ |
58 | #define SOCI_SB 0 | |
59 | #define SOCI_AI 1 | |
60 | ||
523894f2 FL |
61 | /* EROM CompIdentB */ |
62 | #define CIB_REV_MASK 0xff000000 | |
63 | #define CIB_REV_SHIFT 24 | |
64 | ||
1640f28f FL |
65 | /* ARM CR4 core specific control flag bits */ |
66 | #define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020 | |
67 | ||
e12afb6c FL |
68 | #define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) |
69 | /* SDIO Pad drive strength to select value mappings */ | |
70 | struct sdiod_drive_str { | |
71 | u8 strength; /* Pad Drive Strength in mA */ | |
72 | u8 sel; /* Chip-specific select value */ | |
73 | }; | |
ce2d7d7e | 74 | /* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */ |
ffb27565 | 75 | static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = { |
ce2d7d7e FL |
76 | {32, 0x6}, |
77 | {26, 0x7}, | |
78 | {22, 0x4}, | |
79 | {16, 0x5}, | |
80 | {12, 0x2}, | |
81 | {8, 0x3}, | |
82 | {4, 0x0}, | |
83 | {0, 0x1} | |
84 | }; | |
85 | ||
979c2920 HM |
86 | /* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */ |
87 | static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { | |
88 | {16, 0x7}, | |
89 | {12, 0x5}, | |
90 | {8, 0x3}, | |
91 | {4, 0x1} | |
92 | }; | |
93 | ||
99ba15cd FL |
94 | u8 |
95 | brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid) | |
96 | { | |
97 | u8 idx; | |
98 | ||
99 | for (idx = 0; idx < BRCMF_MAX_CORENUM; idx++) | |
100 | if (coreid == ci->c_inf[idx].id) | |
101 | return idx; | |
102 | ||
103 | return BRCMF_MAX_CORENUM; | |
104 | } | |
105 | ||
454d2a88 | 106 | static u32 |
523894f2 FL |
107 | brcmf_sdio_sb_corerev(struct brcmf_sdio_dev *sdiodev, |
108 | struct chip_info *ci, u16 coreid) | |
454d2a88 FL |
109 | { |
110 | u32 regdata; | |
523894f2 FL |
111 | u8 idx; |
112 | ||
113 | idx = brcmf_sdio_chip_getinfidx(ci, coreid); | |
454d2a88 | 114 | |
79ae3957 FL |
115 | regdata = brcmf_sdio_regrl(sdiodev, |
116 | CORE_SB(ci->c_inf[idx].base, sbidhigh), | |
117 | NULL); | |
454d2a88 FL |
118 | return SBCOREREV(regdata); |
119 | } | |
120 | ||
523894f2 FL |
121 | static u32 |
122 | brcmf_sdio_ai_corerev(struct brcmf_sdio_dev *sdiodev, | |
123 | struct chip_info *ci, u16 coreid) | |
124 | { | |
125 | u8 idx; | |
126 | ||
127 | idx = brcmf_sdio_chip_getinfidx(ci, coreid); | |
128 | ||
129 | return (ci->c_inf[idx].cib & CIB_REV_MASK) >> CIB_REV_SHIFT; | |
130 | } | |
131 | ||
6ca687d9 FL |
132 | static bool |
133 | brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev, | |
134 | struct chip_info *ci, u16 coreid) | |
d8f64a42 FL |
135 | { |
136 | u32 regdata; | |
6ca687d9 FL |
137 | u8 idx; |
138 | ||
139 | idx = brcmf_sdio_chip_getinfidx(ci, coreid); | |
74347856 FL |
140 | if (idx == BRCMF_MAX_CORENUM) |
141 | return false; | |
d8f64a42 | 142 | |
79ae3957 FL |
143 | regdata = brcmf_sdio_regrl(sdiodev, |
144 | CORE_SB(ci->c_inf[idx].base, sbtmstatelow), | |
145 | NULL); | |
61213be4 FL |
146 | regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT | |
147 | SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK); | |
148 | return (SSB_TMSLOW_CLOCK == regdata); | |
d8f64a42 FL |
149 | } |
150 | ||
6ca687d9 FL |
151 | static bool |
152 | brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev, | |
153 | struct chip_info *ci, u16 coreid) | |
154 | { | |
155 | u32 regdata; | |
156 | u8 idx; | |
157 | bool ret; | |
158 | ||
159 | idx = brcmf_sdio_chip_getinfidx(ci, coreid); | |
74347856 FL |
160 | if (idx == BRCMF_MAX_CORENUM) |
161 | return false; | |
6ca687d9 | 162 | |
79ae3957 FL |
163 | regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, |
164 | NULL); | |
6ca687d9 FL |
165 | ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK; |
166 | ||
79ae3957 FL |
167 | regdata = brcmf_sdio_regrl(sdiodev, |
168 | ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, | |
169 | NULL); | |
6ca687d9 FL |
170 | ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0); |
171 | ||
172 | return ret; | |
173 | } | |
174 | ||
086a2e0a FL |
175 | static void |
176 | brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev, | |
1640f28f | 177 | struct chip_info *ci, u16 coreid, u32 core_bits) |
2d4a9af1 | 178 | { |
79ae3957 | 179 | u32 regdata, base; |
086a2e0a FL |
180 | u8 idx; |
181 | ||
182 | idx = brcmf_sdio_chip_getinfidx(ci, coreid); | |
79ae3957 | 183 | base = ci->c_inf[idx].base; |
2d4a9af1 | 184 | |
79ae3957 | 185 | regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); |
61213be4 | 186 | if (regdata & SSB_TMSLOW_RESET) |
2d4a9af1 FL |
187 | return; |
188 | ||
79ae3957 | 189 | regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); |
61213be4 | 190 | if ((regdata & SSB_TMSLOW_CLOCK) != 0) { |
2d4a9af1 FL |
191 | /* |
192 | * set target reject and spin until busy is clear | |
193 | * (preserve core-specific bits) | |
194 | */ | |
79ae3957 FL |
195 | regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), |
196 | NULL); | |
e13ce26b FL |
197 | brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow), |
198 | regdata | SSB_TMSLOW_REJECT, NULL); | |
2d4a9af1 | 199 | |
79ae3957 FL |
200 | regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), |
201 | NULL); | |
2d4a9af1 | 202 | udelay(1); |
79ae3957 FL |
203 | SPINWAIT((brcmf_sdio_regrl(sdiodev, |
204 | CORE_SB(base, sbtmstatehigh), | |
205 | NULL) & | |
61213be4 | 206 | SSB_TMSHIGH_BUSY), 100000); |
2d4a9af1 | 207 | |
79ae3957 FL |
208 | regdata = brcmf_sdio_regrl(sdiodev, |
209 | CORE_SB(base, sbtmstatehigh), | |
210 | NULL); | |
61213be4 | 211 | if (regdata & SSB_TMSHIGH_BUSY) |
5e8149f5 | 212 | brcmf_err("core state still busy\n"); |
2d4a9af1 | 213 | |
79ae3957 FL |
214 | regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow), |
215 | NULL); | |
61213be4 | 216 | if (regdata & SSB_IDLOW_INITIATOR) { |
79ae3957 FL |
217 | regdata = brcmf_sdio_regrl(sdiodev, |
218 | CORE_SB(base, sbimstate), | |
219 | NULL); | |
220 | regdata |= SSB_IMSTATE_REJECT; | |
e13ce26b FL |
221 | brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate), |
222 | regdata, NULL); | |
79ae3957 FL |
223 | regdata = brcmf_sdio_regrl(sdiodev, |
224 | CORE_SB(base, sbimstate), | |
225 | NULL); | |
2d4a9af1 | 226 | udelay(1); |
79ae3957 FL |
227 | SPINWAIT((brcmf_sdio_regrl(sdiodev, |
228 | CORE_SB(base, sbimstate), | |
229 | NULL) & | |
61213be4 | 230 | SSB_IMSTATE_BUSY), 100000); |
2d4a9af1 FL |
231 | } |
232 | ||
233 | /* set reset and reject while enabling the clocks */ | |
e13ce26b FL |
234 | regdata = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | |
235 | SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET; | |
236 | brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow), | |
237 | regdata, NULL); | |
79ae3957 FL |
238 | regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), |
239 | NULL); | |
2d4a9af1 FL |
240 | udelay(10); |
241 | ||
242 | /* clear the initiator reject bit */ | |
79ae3957 FL |
243 | regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow), |
244 | NULL); | |
61213be4 | 245 | if (regdata & SSB_IDLOW_INITIATOR) { |
79ae3957 FL |
246 | regdata = brcmf_sdio_regrl(sdiodev, |
247 | CORE_SB(base, sbimstate), | |
248 | NULL); | |
249 | regdata &= ~SSB_IMSTATE_REJECT; | |
e13ce26b FL |
250 | brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate), |
251 | regdata, NULL); | |
2d4a9af1 FL |
252 | } |
253 | } | |
254 | ||
255 | /* leave reset and reject asserted */ | |
e13ce26b FL |
256 | brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow), |
257 | (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET), NULL); | |
2d4a9af1 FL |
258 | udelay(1); |
259 | } | |
260 | ||
086a2e0a FL |
261 | static void |
262 | brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev, | |
1640f28f | 263 | struct chip_info *ci, u16 coreid, u32 core_bits) |
086a2e0a FL |
264 | { |
265 | u8 idx; | |
266 | u32 regdata; | |
267 | ||
268 | idx = brcmf_sdio_chip_getinfidx(ci, coreid); | |
74347856 FL |
269 | if (idx == BRCMF_MAX_CORENUM) |
270 | return; | |
086a2e0a FL |
271 | |
272 | /* if core is already in reset, just return */ | |
79ae3957 FL |
273 | regdata = brcmf_sdio_regrl(sdiodev, |
274 | ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, | |
275 | NULL); | |
086a2e0a FL |
276 | if ((regdata & BCMA_RESET_CTL_RESET) != 0) |
277 | return; | |
278 | ||
1640f28f FL |
279 | /* ensure no pending backplane operation |
280 | * 300uc should be sufficient for backplane ops to be finish | |
281 | * extra 10ms is taken into account for firmware load stage | |
282 | * after 10300us carry on disabling the core anyway | |
283 | */ | |
284 | SPINWAIT(brcmf_sdio_regrl(sdiodev, | |
285 | ci->c_inf[idx].wrapbase+BCMA_RESET_ST, | |
286 | NULL), 10300); | |
287 | regdata = brcmf_sdio_regrl(sdiodev, | |
288 | ci->c_inf[idx].wrapbase+BCMA_RESET_ST, | |
79ae3957 | 289 | NULL); |
1640f28f FL |
290 | if (regdata) |
291 | brcmf_err("disabling core 0x%x with reset status %x\n", | |
292 | coreid, regdata); | |
086a2e0a | 293 | |
e13ce26b FL |
294 | brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, |
295 | BCMA_RESET_CTL_RESET, NULL); | |
086a2e0a | 296 | udelay(1); |
1640f28f FL |
297 | |
298 | brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, | |
299 | core_bits, NULL); | |
300 | regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, | |
301 | NULL); | |
302 | usleep_range(10, 20); | |
303 | ||
086a2e0a FL |
304 | } |
305 | ||
d77e70ff FL |
306 | static void |
307 | brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev, | |
1640f28f | 308 | struct chip_info *ci, u16 coreid, u32 core_bits) |
2bc78e10 FL |
309 | { |
310 | u32 regdata; | |
086a2e0a FL |
311 | u8 idx; |
312 | ||
313 | idx = brcmf_sdio_chip_getinfidx(ci, coreid); | |
74347856 FL |
314 | if (idx == BRCMF_MAX_CORENUM) |
315 | return; | |
2bc78e10 FL |
316 | |
317 | /* | |
318 | * Must do the disable sequence first to work for | |
319 | * arbitrary current core state. | |
320 | */ | |
1640f28f | 321 | brcmf_sdio_sb_coredisable(sdiodev, ci, coreid, 0); |
2bc78e10 FL |
322 | |
323 | /* | |
324 | * Now do the initialization sequence. | |
325 | * set reset while enabling the clock and | |
326 | * forcing them on throughout the core | |
327 | */ | |
e13ce26b FL |
328 | brcmf_sdio_regwl(sdiodev, |
329 | CORE_SB(ci->c_inf[idx].base, sbtmstatelow), | |
330 | SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET, | |
331 | NULL); | |
79ae3957 FL |
332 | regdata = brcmf_sdio_regrl(sdiodev, |
333 | CORE_SB(ci->c_inf[idx].base, sbtmstatelow), | |
334 | NULL); | |
2bc78e10 FL |
335 | udelay(1); |
336 | ||
d77e70ff | 337 | /* clear any serror */ |
79ae3957 FL |
338 | regdata = brcmf_sdio_regrl(sdiodev, |
339 | CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), | |
340 | NULL); | |
61213be4 | 341 | if (regdata & SSB_TMSHIGH_SERR) |
e13ce26b FL |
342 | brcmf_sdio_regwl(sdiodev, |
343 | CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), | |
344 | 0, NULL); | |
2bc78e10 | 345 | |
79ae3957 FL |
346 | regdata = brcmf_sdio_regrl(sdiodev, |
347 | CORE_SB(ci->c_inf[idx].base, sbimstate), | |
348 | NULL); | |
61213be4 | 349 | if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) |
e13ce26b FL |
350 | brcmf_sdio_regwl(sdiodev, |
351 | CORE_SB(ci->c_inf[idx].base, sbimstate), | |
352 | regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO), | |
353 | NULL); | |
2bc78e10 FL |
354 | |
355 | /* clear reset and allow it to propagate throughout the core */ | |
e13ce26b FL |
356 | brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), |
357 | SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK, NULL); | |
79ae3957 FL |
358 | regdata = brcmf_sdio_regrl(sdiodev, |
359 | CORE_SB(ci->c_inf[idx].base, sbtmstatelow), | |
360 | NULL); | |
2bc78e10 FL |
361 | udelay(1); |
362 | ||
363 | /* leave clock enabled */ | |
e13ce26b FL |
364 | brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), |
365 | SSB_TMSLOW_CLOCK, NULL); | |
79ae3957 FL |
366 | regdata = brcmf_sdio_regrl(sdiodev, |
367 | CORE_SB(ci->c_inf[idx].base, sbtmstatelow), | |
368 | NULL); | |
d77e70ff FL |
369 | udelay(1); |
370 | } | |
371 | ||
372 | static void | |
373 | brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev, | |
1640f28f | 374 | struct chip_info *ci, u16 coreid, u32 core_bits) |
d77e70ff FL |
375 | { |
376 | u8 idx; | |
377 | u32 regdata; | |
378 | ||
379 | idx = brcmf_sdio_chip_getinfidx(ci, coreid); | |
74347856 FL |
380 | if (idx == BRCMF_MAX_CORENUM) |
381 | return; | |
d77e70ff FL |
382 | |
383 | /* must disable first to work for arbitrary current core state */ | |
1640f28f | 384 | brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, core_bits); |
d77e70ff FL |
385 | |
386 | /* now do initialization sequence */ | |
e13ce26b | 387 | brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, |
1640f28f | 388 | core_bits | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL); |
79ae3957 FL |
389 | regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, |
390 | NULL); | |
e13ce26b FL |
391 | brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, |
392 | 0, NULL); | |
1640f28f FL |
393 | regdata = brcmf_sdio_regrl(sdiodev, |
394 | ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, | |
395 | NULL); | |
d77e70ff FL |
396 | udelay(1); |
397 | ||
e13ce26b | 398 | brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, |
1640f28f | 399 | core_bits | BCMA_IOCTL_CLK, NULL); |
79ae3957 FL |
400 | regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, |
401 | NULL); | |
2bc78e10 FL |
402 | udelay(1); |
403 | } | |
404 | ||
1640f28f FL |
405 | #ifdef DEBUG |
406 | /* safety check for chipinfo */ | |
407 | static int brcmf_sdio_chip_cichk(struct chip_info *ci) | |
408 | { | |
409 | u8 core_idx; | |
410 | ||
411 | /* check RAM core presence for ARM CM3 core */ | |
412 | core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3); | |
413 | if (BRCMF_MAX_CORENUM != core_idx) { | |
414 | core_idx = brcmf_sdio_chip_getinfidx(ci, | |
415 | BCMA_CORE_INTERNAL_MEM); | |
416 | if (BRCMF_MAX_CORENUM == core_idx) { | |
417 | brcmf_err("RAM core not provided with ARM CM3 core\n"); | |
418 | return -ENODEV; | |
419 | } | |
420 | } | |
421 | ||
422 | /* check RAM base for ARM CR4 core */ | |
423 | core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CR4); | |
424 | if (BRCMF_MAX_CORENUM != core_idx) { | |
425 | if (ci->rambase == 0) { | |
426 | brcmf_err("RAM base not provided with ARM CR4 core\n"); | |
427 | return -ENOMEM; | |
428 | } | |
429 | } | |
430 | ||
431 | return 0; | |
432 | } | |
433 | #else /* DEBUG */ | |
434 | static inline int brcmf_sdio_chip_cichk(struct chip_info *ci) | |
435 | { | |
436 | return 0; | |
437 | } | |
438 | #endif | |
439 | ||
a83369b6 FL |
440 | static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, |
441 | struct chip_info *ci, u32 regs) | |
442 | { | |
443 | u32 regdata; | |
1640f28f | 444 | int ret; |
a83369b6 | 445 | |
069eddd9 | 446 | /* Get CC core rev |
a83369b6 FL |
447 | * Chipid is assume to be at offset 0 from regs arg |
448 | * For different chiptypes or old sdio hosts w/o chipcommon, | |
449 | * other ways of recognition should be added here. | |
450 | */ | |
99ba15cd FL |
451 | ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON; |
452 | ci->c_inf[0].base = regs; | |
79ae3957 FL |
453 | regdata = brcmf_sdio_regrl(sdiodev, |
454 | CORE_CC_REG(ci->c_inf[0].base, chipid), | |
455 | NULL); | |
a83369b6 FL |
456 | ci->chip = regdata & CID_ID_MASK; |
457 | ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; | |
fe040158 FL |
458 | if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && |
459 | ci->chiprev >= 2) | |
460 | ci->chip = BCM4339_CHIP_ID; | |
6ca687d9 | 461 | ci->socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT; |
a83369b6 FL |
462 | |
463 | brcmf_dbg(INFO, "chipid=0x%x chiprev=%d\n", ci->chip, ci->chiprev); | |
464 | ||
465 | /* Address of cores for new chips should be added here */ | |
466 | switch (ci->chip) { | |
369508c5 HM |
467 | case BCM43143_CHIP_ID: |
468 | ci->c_inf[0].wrapbase = ci->c_inf[0].base + 0x00100000; | |
469 | ci->c_inf[0].cib = 0x2b000000; | |
470 | ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; | |
471 | ci->c_inf[1].base = BCM43143_CORE_BUS_BASE; | |
472 | ci->c_inf[1].wrapbase = ci->c_inf[1].base + 0x00100000; | |
473 | ci->c_inf[1].cib = 0x18000000; | |
474 | ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; | |
475 | ci->c_inf[2].base = BCM43143_CORE_SOCRAM_BASE; | |
476 | ci->c_inf[2].wrapbase = ci->c_inf[2].base + 0x00100000; | |
477 | ci->c_inf[2].cib = 0x14000000; | |
478 | ci->c_inf[3].id = BCMA_CORE_ARM_CM3; | |
479 | ci->c_inf[3].base = BCM43143_CORE_ARM_BASE; | |
480 | ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; | |
481 | ci->c_inf[3].cib = 0x07000000; | |
482 | ci->ramsize = BCM43143_RAMSIZE; | |
483 | break; | |
4a1c02ce FL |
484 | case BCM43241_CHIP_ID: |
485 | ci->c_inf[0].wrapbase = 0x18100000; | |
486 | ci->c_inf[0].cib = 0x2a084411; | |
487 | ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; | |
488 | ci->c_inf[1].base = 0x18002000; | |
489 | ci->c_inf[1].wrapbase = 0x18102000; | |
490 | ci->c_inf[1].cib = 0x0e004211; | |
491 | ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; | |
492 | ci->c_inf[2].base = 0x18004000; | |
493 | ci->c_inf[2].wrapbase = 0x18104000; | |
494 | ci->c_inf[2].cib = 0x14080401; | |
495 | ci->c_inf[3].id = BCMA_CORE_ARM_CM3; | |
496 | ci->c_inf[3].base = 0x18003000; | |
497 | ci->c_inf[3].wrapbase = 0x18103000; | |
498 | ci->c_inf[3].cib = 0x07004211; | |
499 | ci->ramsize = 0x90000; | |
500 | break; | |
a83369b6 | 501 | case BCM4329_CHIP_ID: |
99ba15cd FL |
502 | ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; |
503 | ci->c_inf[1].base = BCM4329_CORE_BUS_BASE; | |
504 | ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; | |
505 | ci->c_inf[2].base = BCM4329_CORE_SOCRAM_BASE; | |
506 | ci->c_inf[3].id = BCMA_CORE_ARM_CM3; | |
507 | ci->c_inf[3].base = BCM4329_CORE_ARM_BASE; | |
a83369b6 FL |
508 | ci->ramsize = BCM4329_RAMSIZE; |
509 | break; | |
ce2d7d7e FL |
510 | case BCM4330_CHIP_ID: |
511 | ci->c_inf[0].wrapbase = 0x18100000; | |
512 | ci->c_inf[0].cib = 0x27004211; | |
513 | ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; | |
514 | ci->c_inf[1].base = 0x18002000; | |
515 | ci->c_inf[1].wrapbase = 0x18102000; | |
516 | ci->c_inf[1].cib = 0x07004211; | |
517 | ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; | |
518 | ci->c_inf[2].base = 0x18004000; | |
519 | ci->c_inf[2].wrapbase = 0x18104000; | |
520 | ci->c_inf[2].cib = 0x0d080401; | |
521 | ci->c_inf[3].id = BCMA_CORE_ARM_CM3; | |
522 | ci->c_inf[3].base = 0x18003000; | |
523 | ci->c_inf[3].wrapbase = 0x18103000; | |
524 | ci->c_inf[3].cib = 0x03004211; | |
525 | ci->ramsize = 0x48000; | |
526 | break; | |
85a4a1c3 FL |
527 | case BCM4334_CHIP_ID: |
528 | ci->c_inf[0].wrapbase = 0x18100000; | |
529 | ci->c_inf[0].cib = 0x29004211; | |
530 | ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; | |
531 | ci->c_inf[1].base = 0x18002000; | |
532 | ci->c_inf[1].wrapbase = 0x18102000; | |
533 | ci->c_inf[1].cib = 0x0d004211; | |
534 | ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; | |
535 | ci->c_inf[2].base = 0x18004000; | |
536 | ci->c_inf[2].wrapbase = 0x18104000; | |
537 | ci->c_inf[2].cib = 0x13080401; | |
538 | ci->c_inf[3].id = BCMA_CORE_ARM_CM3; | |
539 | ci->c_inf[3].base = 0x18003000; | |
540 | ci->c_inf[3].wrapbase = 0x18103000; | |
541 | ci->c_inf[3].cib = 0x07004211; | |
542 | ci->ramsize = 0x80000; | |
543 | break; | |
6a1c7483 FL |
544 | case BCM4335_CHIP_ID: |
545 | ci->c_inf[0].wrapbase = 0x18100000; | |
546 | ci->c_inf[0].cib = 0x2b084411; | |
547 | ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; | |
548 | ci->c_inf[1].base = 0x18005000; | |
549 | ci->c_inf[1].wrapbase = 0x18105000; | |
550 | ci->c_inf[1].cib = 0x0f004211; | |
551 | ci->c_inf[2].id = BCMA_CORE_ARM_CR4; | |
552 | ci->c_inf[2].base = 0x18002000; | |
553 | ci->c_inf[2].wrapbase = 0x18102000; | |
554 | ci->c_inf[2].cib = 0x01084411; | |
555 | ci->ramsize = 0xc0000; | |
556 | ci->rambase = 0x180000; | |
557 | break; | |
fe040158 FL |
558 | case BCM4339_CHIP_ID: |
559 | ci->c_inf[0].wrapbase = 0x18100000; | |
560 | ci->c_inf[0].cib = 0x2e084411; | |
561 | ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; | |
562 | ci->c_inf[1].base = 0x18005000; | |
563 | ci->c_inf[1].wrapbase = 0x18105000; | |
564 | ci->c_inf[1].cib = 0x15004211; | |
565 | ci->c_inf[2].id = BCMA_CORE_ARM_CR4; | |
566 | ci->c_inf[2].base = 0x18002000; | |
567 | ci->c_inf[2].wrapbase = 0x18102000; | |
568 | ci->c_inf[2].cib = 0x04084411; | |
569 | ci->ramsize = 0xc0000; | |
570 | ci->rambase = 0x180000; | |
571 | break; | |
a83369b6 | 572 | default: |
5e8149f5 | 573 | brcmf_err("chipid 0x%x is not supported\n", ci->chip); |
a83369b6 FL |
574 | return -ENODEV; |
575 | } | |
576 | ||
1640f28f FL |
577 | ret = brcmf_sdio_chip_cichk(ci); |
578 | if (ret) | |
579 | return ret; | |
580 | ||
6ca687d9 FL |
581 | switch (ci->socitype) { |
582 | case SOCI_SB: | |
583 | ci->iscoreup = brcmf_sdio_sb_iscoreup; | |
523894f2 | 584 | ci->corerev = brcmf_sdio_sb_corerev; |
086a2e0a | 585 | ci->coredisable = brcmf_sdio_sb_coredisable; |
d77e70ff | 586 | ci->resetcore = brcmf_sdio_sb_resetcore; |
6ca687d9 FL |
587 | break; |
588 | case SOCI_AI: | |
589 | ci->iscoreup = brcmf_sdio_ai_iscoreup; | |
523894f2 | 590 | ci->corerev = brcmf_sdio_ai_corerev; |
086a2e0a | 591 | ci->coredisable = brcmf_sdio_ai_coredisable; |
d77e70ff | 592 | ci->resetcore = brcmf_sdio_ai_resetcore; |
6ca687d9 FL |
593 | break; |
594 | default: | |
5e8149f5 | 595 | brcmf_err("socitype %u not supported\n", ci->socitype); |
6ca687d9 FL |
596 | return -ENODEV; |
597 | } | |
598 | ||
a83369b6 FL |
599 | return 0; |
600 | } | |
601 | ||
e63ac6b8 FL |
602 | static int |
603 | brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev) | |
604 | { | |
605 | int err = 0; | |
606 | u8 clkval, clkset; | |
607 | ||
608 | /* Try forcing SDIO core to do ALPAvail request only */ | |
609 | clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; | |
3bba829f | 610 | brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); |
e63ac6b8 | 611 | if (err) { |
5e8149f5 | 612 | brcmf_err("error writing for HT off\n"); |
e63ac6b8 FL |
613 | return err; |
614 | } | |
615 | ||
616 | /* If register supported, wait for ALPAvail and then force ALP */ | |
617 | /* This may take up to 15 milliseconds */ | |
45db339c FL |
618 | clkval = brcmf_sdio_regrb(sdiodev, |
619 | SBSDIO_FUNC1_CHIPCLKCSR, NULL); | |
e63ac6b8 FL |
620 | |
621 | if ((clkval & ~SBSDIO_AVBITS) != clkset) { | |
5e8149f5 | 622 | brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n", |
e63ac6b8 FL |
623 | clkset, clkval); |
624 | return -EACCES; | |
625 | } | |
626 | ||
45db339c FL |
627 | SPINWAIT(((clkval = brcmf_sdio_regrb(sdiodev, |
628 | SBSDIO_FUNC1_CHIPCLKCSR, NULL)), | |
e63ac6b8 FL |
629 | !SBSDIO_ALPAV(clkval)), |
630 | PMU_MAX_TRANSITION_DLY); | |
631 | if (!SBSDIO_ALPAV(clkval)) { | |
5e8149f5 | 632 | brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n", |
e63ac6b8 FL |
633 | clkval); |
634 | return -EBUSY; | |
635 | } | |
636 | ||
637 | clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; | |
3bba829f | 638 | brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); |
e63ac6b8 FL |
639 | udelay(65); |
640 | ||
641 | /* Also, disable the extra SDIO pull-ups */ | |
3bba829f | 642 | brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); |
e63ac6b8 FL |
643 | |
644 | return 0; | |
645 | } | |
646 | ||
5b45e54e FL |
647 | static void |
648 | brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev, | |
649 | struct chip_info *ci) | |
650 | { | |
79ae3957 FL |
651 | u32 base = ci->c_inf[0].base; |
652 | ||
5b45e54e | 653 | /* get chipcommon rev */ |
523894f2 | 654 | ci->c_inf[0].rev = ci->corerev(sdiodev, ci, ci->c_inf[0].id); |
5b45e54e FL |
655 | |
656 | /* get chipcommon capabilites */ | |
79ae3957 FL |
657 | ci->c_inf[0].caps = brcmf_sdio_regrl(sdiodev, |
658 | CORE_CC_REG(base, capabilities), | |
659 | NULL); | |
5b45e54e FL |
660 | |
661 | /* get pmu caps & rev */ | |
99ba15cd | 662 | if (ci->c_inf[0].caps & CC_CAP_PMU) { |
79ae3957 FL |
663 | ci->pmucaps = |
664 | brcmf_sdio_regrl(sdiodev, | |
665 | CORE_CC_REG(base, pmucapabilities), | |
666 | NULL); | |
5b45e54e FL |
667 | ci->pmurev = ci->pmucaps & PCAP_REV_MASK; |
668 | } | |
669 | ||
523894f2 | 670 | ci->c_inf[1].rev = ci->corerev(sdiodev, ci, ci->c_inf[1].id); |
5b45e54e FL |
671 | |
672 | brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n", | |
99ba15cd FL |
673 | ci->c_inf[0].rev, ci->pmurev, |
674 | ci->c_inf[1].rev, ci->c_inf[1].id); | |
966414da FL |
675 | |
676 | /* | |
677 | * Make sure any on-chip ARM is off (in case strapping is wrong), | |
678 | * or downloaded code was already running. | |
679 | */ | |
1640f28f | 680 | ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0); |
5b45e54e FL |
681 | } |
682 | ||
a83369b6 | 683 | int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, |
a97e4fc5 | 684 | struct chip_info **ci_ptr, u32 regs) |
a83369b6 | 685 | { |
a97e4fc5 FL |
686 | int ret; |
687 | struct chip_info *ci; | |
688 | ||
689 | brcmf_dbg(TRACE, "Enter\n"); | |
690 | ||
691 | /* alloc chip_info_t */ | |
692 | ci = kzalloc(sizeof(struct chip_info), GFP_ATOMIC); | |
693 | if (!ci) | |
694 | return -ENOMEM; | |
a83369b6 | 695 | |
e63ac6b8 FL |
696 | ret = brcmf_sdio_chip_buscoreprep(sdiodev); |
697 | if (ret != 0) | |
a97e4fc5 | 698 | goto err; |
e63ac6b8 | 699 | |
a83369b6 FL |
700 | ret = brcmf_sdio_chip_recognition(sdiodev, ci, regs); |
701 | if (ret != 0) | |
a97e4fc5 | 702 | goto err; |
a83369b6 | 703 | |
5b45e54e FL |
704 | brcmf_sdio_chip_buscoresetup(sdiodev, ci); |
705 | ||
e13ce26b FL |
706 | brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopullup), |
707 | 0, NULL); | |
708 | brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopulldown), | |
709 | 0, NULL); | |
960908dc | 710 | |
a97e4fc5 FL |
711 | *ci_ptr = ci; |
712 | return 0; | |
713 | ||
714 | err: | |
715 | kfree(ci); | |
a83369b6 FL |
716 | return ret; |
717 | } | |
a8a6c045 FL |
718 | |
719 | void | |
720 | brcmf_sdio_chip_detach(struct chip_info **ci_ptr) | |
721 | { | |
722 | brcmf_dbg(TRACE, "Enter\n"); | |
723 | ||
724 | kfree(*ci_ptr); | |
725 | *ci_ptr = NULL; | |
726 | } | |
e12afb6c FL |
727 | |
728 | static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len) | |
729 | { | |
730 | const char *fmt; | |
731 | ||
732 | fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; | |
733 | snprintf(buf, len, fmt, chipid); | |
734 | return buf; | |
735 | } | |
736 | ||
737 | void | |
738 | brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, | |
739 | struct chip_info *ci, u32 drivestrength) | |
740 | { | |
979c2920 HM |
741 | const struct sdiod_drive_str *str_tab = NULL; |
742 | u32 str_mask; | |
743 | u32 str_shift; | |
e12afb6c | 744 | char chn[8]; |
79ae3957 | 745 | u32 base = ci->c_inf[0].base; |
979c2920 HM |
746 | u32 i; |
747 | u32 drivestrength_sel = 0; | |
748 | u32 cc_data_temp; | |
749 | u32 addr; | |
e12afb6c | 750 | |
99ba15cd | 751 | if (!(ci->c_inf[0].caps & CC_CAP_PMU)) |
e12afb6c FL |
752 | return; |
753 | ||
754 | switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { | |
ce2d7d7e | 755 | case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12): |
979c2920 | 756 | str_tab = sdiod_drvstr_tab1_1v8; |
ce2d7d7e FL |
757 | str_mask = 0x00003800; |
758 | str_shift = 11; | |
759 | break; | |
979c2920 HM |
760 | case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17): |
761 | /* note: 43143 does not support tristate */ | |
762 | i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1; | |
763 | if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) { | |
764 | str_tab = sdiod_drvstr_tab2_3v3; | |
765 | str_mask = 0x00000007; | |
766 | str_shift = 0; | |
767 | } else | |
768 | brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n", | |
769 | brcmf_sdio_chip_name(ci->chip, chn, 8), | |
770 | drivestrength); | |
771 | break; | |
e12afb6c | 772 | default: |
5e8149f5 | 773 | brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", |
e12afb6c FL |
774 | brcmf_sdio_chip_name(ci->chip, chn, 8), |
775 | ci->chiprev, ci->pmurev); | |
776 | break; | |
777 | } | |
778 | ||
779 | if (str_tab != NULL) { | |
e12afb6c FL |
780 | for (i = 0; str_tab[i].strength != 0; i++) { |
781 | if (drivestrength >= str_tab[i].strength) { | |
782 | drivestrength_sel = str_tab[i].sel; | |
783 | break; | |
784 | } | |
785 | } | |
979c2920 HM |
786 | addr = CORE_CC_REG(base, chipcontrol_addr); |
787 | brcmf_sdio_regwl(sdiodev, addr, 1, NULL); | |
788 | cc_data_temp = brcmf_sdio_regrl(sdiodev, addr, NULL); | |
e12afb6c FL |
789 | cc_data_temp &= ~str_mask; |
790 | drivestrength_sel <<= str_shift; | |
791 | cc_data_temp |= drivestrength_sel; | |
979c2920 | 792 | brcmf_sdio_regwl(sdiodev, addr, cc_data_temp, NULL); |
e12afb6c | 793 | |
979c2920 HM |
794 | brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n", |
795 | str_tab[i].strength, drivestrength, cc_data_temp); | |
e12afb6c FL |
796 | } |
797 | } | |
069eddd9 FL |
798 | |
799 | #ifdef DEBUG | |
800 | static bool | |
801 | brcmf_sdio_chip_verifynvram(struct brcmf_sdio_dev *sdiodev, u32 nvram_addr, | |
802 | char *nvram_dat, uint nvram_sz) | |
803 | { | |
804 | char *nvram_ularray; | |
805 | int err; | |
806 | bool ret = true; | |
807 | ||
808 | /* read back and verify */ | |
809 | brcmf_dbg(INFO, "Compare NVRAM dl & ul; size=%d\n", nvram_sz); | |
810 | nvram_ularray = kmalloc(nvram_sz, GFP_KERNEL); | |
811 | /* do not proceed while no memory but */ | |
812 | if (!nvram_ularray) | |
813 | return true; | |
814 | ||
815 | /* Upload image to verify downloaded contents. */ | |
816 | memset(nvram_ularray, 0xaa, nvram_sz); | |
817 | ||
818 | /* Read the vars list to temp buffer for comparison */ | |
819 | err = brcmf_sdio_ramrw(sdiodev, false, nvram_addr, nvram_ularray, | |
820 | nvram_sz); | |
821 | if (err) { | |
822 | brcmf_err("error %d on reading %d nvram bytes at 0x%08x\n", | |
823 | err, nvram_sz, nvram_addr); | |
824 | } else if (memcmp(nvram_dat, nvram_ularray, nvram_sz)) { | |
825 | brcmf_err("Downloaded NVRAM image is corrupted\n"); | |
826 | ret = false; | |
827 | } | |
828 | kfree(nvram_ularray); | |
829 | ||
830 | return ret; | |
831 | } | |
832 | #else /* DEBUG */ | |
833 | static inline bool | |
834 | brcmf_sdio_chip_verifynvram(struct brcmf_sdio_dev *sdiodev, u32 nvram_addr, | |
835 | char *nvram_dat, uint nvram_sz) | |
836 | { | |
837 | return true; | |
838 | } | |
839 | #endif /* DEBUG */ | |
840 | ||
841 | static bool brcmf_sdio_chip_writenvram(struct brcmf_sdio_dev *sdiodev, | |
842 | struct chip_info *ci, | |
843 | char *nvram_dat, uint nvram_sz) | |
844 | { | |
845 | int err; | |
846 | u32 nvram_addr; | |
847 | u32 token; | |
848 | __le32 token_le; | |
849 | ||
1640f28f | 850 | nvram_addr = (ci->ramsize - 4) - nvram_sz + ci->rambase; |
069eddd9 FL |
851 | |
852 | /* Write the vars list */ | |
853 | err = brcmf_sdio_ramrw(sdiodev, true, nvram_addr, nvram_dat, nvram_sz); | |
854 | if (err) { | |
855 | brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n", | |
856 | err, nvram_sz, nvram_addr); | |
857 | return false; | |
858 | } | |
859 | ||
860 | if (!brcmf_sdio_chip_verifynvram(sdiodev, nvram_addr, | |
861 | nvram_dat, nvram_sz)) | |
862 | return false; | |
863 | ||
864 | /* generate token: | |
865 | * nvram size, converted to words, in lower 16-bits, checksum | |
866 | * in upper 16-bits. | |
867 | */ | |
868 | token = nvram_sz / 4; | |
869 | token = (~token << 16) | (token & 0x0000FFFF); | |
870 | token_le = cpu_to_le32(token); | |
871 | ||
872 | brcmf_dbg(INFO, "RAM size: %d\n", ci->ramsize); | |
873 | brcmf_dbg(INFO, "nvram is placed at %d, size %d, token=0x%08x\n", | |
874 | nvram_addr, nvram_sz, token); | |
875 | ||
876 | /* Write the length token to the last word */ | |
1640f28f | 877 | if (brcmf_sdio_ramrw(sdiodev, true, (ci->ramsize - 4 + ci->rambase), |
069eddd9 FL |
878 | (u8 *)&token_le, 4)) |
879 | return false; | |
880 | ||
881 | return true; | |
882 | } | |
883 | ||
884 | static void | |
885 | brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev, | |
886 | struct chip_info *ci) | |
887 | { | |
888 | u32 zeros = 0; | |
889 | ||
1640f28f FL |
890 | ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0); |
891 | ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0); | |
069eddd9 FL |
892 | |
893 | /* clear length token */ | |
894 | brcmf_sdio_ramrw(sdiodev, true, ci->ramsize - 4, (u8 *)&zeros, 4); | |
895 | } | |
896 | ||
897 | static bool | |
898 | brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, | |
899 | char *nvram_dat, uint nvram_sz) | |
900 | { | |
901 | u8 core_idx; | |
902 | u32 reg_addr; | |
903 | ||
904 | if (!ci->iscoreup(sdiodev, ci, BCMA_CORE_INTERNAL_MEM)) { | |
905 | brcmf_err("SOCRAM core is down after reset?\n"); | |
906 | return false; | |
907 | } | |
908 | ||
909 | if (!brcmf_sdio_chip_writenvram(sdiodev, ci, nvram_dat, nvram_sz)) | |
910 | return false; | |
911 | ||
912 | /* clear all interrupts */ | |
913 | core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV); | |
914 | reg_addr = ci->c_inf[core_idx].base; | |
915 | reg_addr += offsetof(struct sdpcmd_regs, intstatus); | |
916 | brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); | |
917 | ||
1640f28f FL |
918 | ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3, 0); |
919 | ||
920 | return true; | |
921 | } | |
922 | ||
923 | static inline void | |
924 | brcmf_sdio_chip_cr4_enterdl(struct brcmf_sdio_dev *sdiodev, | |
925 | struct chip_info *ci) | |
926 | { | |
927 | ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, | |
928 | ARMCR4_BCMA_IOCTL_CPUHALT); | |
929 | } | |
930 | ||
931 | static bool | |
932 | brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, | |
933 | char *nvram_dat, uint nvram_sz) | |
934 | { | |
935 | u8 core_idx; | |
936 | u32 reg_addr; | |
937 | ||
938 | if (!brcmf_sdio_chip_writenvram(sdiodev, ci, nvram_dat, nvram_sz)) | |
939 | return false; | |
940 | ||
941 | /* clear all interrupts */ | |
942 | core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV); | |
943 | reg_addr = ci->c_inf[core_idx].base; | |
944 | reg_addr += offsetof(struct sdpcmd_regs, intstatus); | |
945 | brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); | |
946 | ||
947 | /* Write reset vector to address 0 */ | |
948 | brcmf_sdio_ramrw(sdiodev, true, 0, (void *)&ci->rst_vec, | |
949 | sizeof(ci->rst_vec)); | |
950 | ||
951 | /* restore ARM */ | |
952 | ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, 0); | |
069eddd9 FL |
953 | |
954 | return true; | |
955 | } | |
956 | ||
957 | void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, | |
958 | struct chip_info *ci) | |
959 | { | |
1640f28f FL |
960 | u8 arm_core_idx; |
961 | ||
962 | arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3); | |
963 | if (BRCMF_MAX_CORENUM != arm_core_idx) { | |
964 | brcmf_sdio_chip_cm3_enterdl(sdiodev, ci); | |
965 | return; | |
966 | } | |
967 | ||
968 | brcmf_sdio_chip_cr4_enterdl(sdiodev, ci); | |
069eddd9 FL |
969 | } |
970 | ||
971 | bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev, | |
972 | struct chip_info *ci, char *nvram_dat, | |
973 | uint nvram_sz) | |
974 | { | |
1640f28f FL |
975 | u8 arm_core_idx; |
976 | ||
977 | arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3); | |
978 | if (BRCMF_MAX_CORENUM != arm_core_idx) | |
979 | return brcmf_sdio_chip_cm3_exitdl(sdiodev, ci, nvram_dat, | |
980 | nvram_sz); | |
981 | ||
982 | return brcmf_sdio_chip_cr4_exitdl(sdiodev, ci, nvram_dat, nvram_sz); | |
069eddd9 | 983 | } |