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