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 | ||
a83369b6 | 43 | #define SBCOREREV(sbidh) \ |
61213be4 FL |
44 | ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \ |
45 | ((sbidh) & SSB_IDHIGH_RCLO)) | |
a83369b6 | 46 | |
6ca687d9 FL |
47 | /* SOC Interconnect types (aka chip types) */ |
48 | #define SOCI_SB 0 | |
49 | #define SOCI_AI 1 | |
50 | ||
523894f2 FL |
51 | /* EROM CompIdentB */ |
52 | #define CIB_REV_MASK 0xff000000 | |
53 | #define CIB_REV_SHIFT 24 | |
54 | ||
e12afb6c FL |
55 | #define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) |
56 | /* SDIO Pad drive strength to select value mappings */ | |
57 | struct sdiod_drive_str { | |
58 | u8 strength; /* Pad Drive Strength in mA */ | |
59 | u8 sel; /* Chip-specific select value */ | |
60 | }; | |
ce2d7d7e | 61 | /* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */ |
ffb27565 | 62 | static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = { |
ce2d7d7e FL |
63 | {32, 0x6}, |
64 | {26, 0x7}, | |
65 | {22, 0x4}, | |
66 | {16, 0x5}, | |
67 | {12, 0x2}, | |
68 | {8, 0x3}, | |
69 | {4, 0x0}, | |
70 | {0, 0x1} | |
71 | }; | |
72 | ||
99ba15cd FL |
73 | u8 |
74 | brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid) | |
75 | { | |
76 | u8 idx; | |
77 | ||
78 | for (idx = 0; idx < BRCMF_MAX_CORENUM; idx++) | |
79 | if (coreid == ci->c_inf[idx].id) | |
80 | return idx; | |
81 | ||
82 | return BRCMF_MAX_CORENUM; | |
83 | } | |
84 | ||
454d2a88 | 85 | static u32 |
523894f2 FL |
86 | brcmf_sdio_sb_corerev(struct brcmf_sdio_dev *sdiodev, |
87 | struct chip_info *ci, u16 coreid) | |
454d2a88 FL |
88 | { |
89 | u32 regdata; | |
523894f2 FL |
90 | u8 idx; |
91 | ||
92 | idx = brcmf_sdio_chip_getinfidx(ci, coreid); | |
454d2a88 | 93 | |
79ae3957 FL |
94 | regdata = brcmf_sdio_regrl(sdiodev, |
95 | CORE_SB(ci->c_inf[idx].base, sbidhigh), | |
96 | NULL); | |
454d2a88 FL |
97 | return SBCOREREV(regdata); |
98 | } | |
99 | ||
523894f2 FL |
100 | static u32 |
101 | brcmf_sdio_ai_corerev(struct brcmf_sdio_dev *sdiodev, | |
102 | struct chip_info *ci, u16 coreid) | |
103 | { | |
104 | u8 idx; | |
105 | ||
106 | idx = brcmf_sdio_chip_getinfidx(ci, coreid); | |
107 | ||
108 | return (ci->c_inf[idx].cib & CIB_REV_MASK) >> CIB_REV_SHIFT; | |
109 | } | |
110 | ||
6ca687d9 FL |
111 | static bool |
112 | brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev, | |
113 | struct chip_info *ci, u16 coreid) | |
d8f64a42 FL |
114 | { |
115 | u32 regdata; | |
6ca687d9 FL |
116 | u8 idx; |
117 | ||
118 | idx = brcmf_sdio_chip_getinfidx(ci, coreid); | |
d8f64a42 | 119 | |
79ae3957 FL |
120 | regdata = brcmf_sdio_regrl(sdiodev, |
121 | CORE_SB(ci->c_inf[idx].base, sbtmstatelow), | |
122 | NULL); | |
61213be4 FL |
123 | regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT | |
124 | SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK); | |
125 | return (SSB_TMSLOW_CLOCK == regdata); | |
d8f64a42 FL |
126 | } |
127 | ||
6ca687d9 FL |
128 | static bool |
129 | brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev, | |
130 | struct chip_info *ci, u16 coreid) | |
131 | { | |
132 | u32 regdata; | |
133 | u8 idx; | |
134 | bool ret; | |
135 | ||
136 | idx = brcmf_sdio_chip_getinfidx(ci, coreid); | |
137 | ||
79ae3957 FL |
138 | regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, |
139 | NULL); | |
6ca687d9 FL |
140 | ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK; |
141 | ||
79ae3957 FL |
142 | regdata = brcmf_sdio_regrl(sdiodev, |
143 | ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, | |
144 | NULL); | |
6ca687d9 FL |
145 | ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0); |
146 | ||
147 | return ret; | |
148 | } | |
149 | ||
086a2e0a FL |
150 | static void |
151 | brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev, | |
152 | struct chip_info *ci, u16 coreid) | |
2d4a9af1 | 153 | { |
79ae3957 | 154 | u32 regdata, base; |
086a2e0a FL |
155 | u8 idx; |
156 | ||
157 | idx = brcmf_sdio_chip_getinfidx(ci, coreid); | |
79ae3957 | 158 | base = ci->c_inf[idx].base; |
2d4a9af1 | 159 | |
79ae3957 | 160 | regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); |
61213be4 | 161 | if (regdata & SSB_TMSLOW_RESET) |
2d4a9af1 FL |
162 | return; |
163 | ||
79ae3957 | 164 | regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); |
61213be4 | 165 | if ((regdata & SSB_TMSLOW_CLOCK) != 0) { |
2d4a9af1 FL |
166 | /* |
167 | * set target reject and spin until busy is clear | |
168 | * (preserve core-specific bits) | |
169 | */ | |
79ae3957 FL |
170 | regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), |
171 | NULL); | |
e13ce26b FL |
172 | brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow), |
173 | regdata | SSB_TMSLOW_REJECT, NULL); | |
2d4a9af1 | 174 | |
79ae3957 FL |
175 | regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), |
176 | NULL); | |
2d4a9af1 | 177 | udelay(1); |
79ae3957 FL |
178 | SPINWAIT((brcmf_sdio_regrl(sdiodev, |
179 | CORE_SB(base, sbtmstatehigh), | |
180 | NULL) & | |
61213be4 | 181 | SSB_TMSHIGH_BUSY), 100000); |
2d4a9af1 | 182 | |
79ae3957 FL |
183 | regdata = brcmf_sdio_regrl(sdiodev, |
184 | CORE_SB(base, sbtmstatehigh), | |
185 | NULL); | |
61213be4 | 186 | if (regdata & SSB_TMSHIGH_BUSY) |
5e8149f5 | 187 | brcmf_err("core state still busy\n"); |
2d4a9af1 | 188 | |
79ae3957 FL |
189 | regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow), |
190 | NULL); | |
61213be4 | 191 | if (regdata & SSB_IDLOW_INITIATOR) { |
79ae3957 FL |
192 | regdata = brcmf_sdio_regrl(sdiodev, |
193 | CORE_SB(base, sbimstate), | |
194 | NULL); | |
195 | regdata |= SSB_IMSTATE_REJECT; | |
e13ce26b FL |
196 | brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate), |
197 | regdata, NULL); | |
79ae3957 FL |
198 | regdata = brcmf_sdio_regrl(sdiodev, |
199 | CORE_SB(base, sbimstate), | |
200 | NULL); | |
2d4a9af1 | 201 | udelay(1); |
79ae3957 FL |
202 | SPINWAIT((brcmf_sdio_regrl(sdiodev, |
203 | CORE_SB(base, sbimstate), | |
204 | NULL) & | |
61213be4 | 205 | SSB_IMSTATE_BUSY), 100000); |
2d4a9af1 FL |
206 | } |
207 | ||
208 | /* set reset and reject while enabling the clocks */ | |
e13ce26b FL |
209 | regdata = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | |
210 | SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET; | |
211 | brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow), | |
212 | regdata, NULL); | |
79ae3957 FL |
213 | regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), |
214 | NULL); | |
2d4a9af1 FL |
215 | udelay(10); |
216 | ||
217 | /* clear the initiator reject bit */ | |
79ae3957 FL |
218 | regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow), |
219 | NULL); | |
61213be4 | 220 | if (regdata & SSB_IDLOW_INITIATOR) { |
79ae3957 FL |
221 | regdata = brcmf_sdio_regrl(sdiodev, |
222 | CORE_SB(base, sbimstate), | |
223 | NULL); | |
224 | regdata &= ~SSB_IMSTATE_REJECT; | |
e13ce26b FL |
225 | brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate), |
226 | regdata, NULL); | |
2d4a9af1 FL |
227 | } |
228 | } | |
229 | ||
230 | /* leave reset and reject asserted */ | |
e13ce26b FL |
231 | brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow), |
232 | (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET), NULL); | |
2d4a9af1 FL |
233 | udelay(1); |
234 | } | |
235 | ||
086a2e0a FL |
236 | static void |
237 | brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev, | |
238 | struct chip_info *ci, u16 coreid) | |
239 | { | |
240 | u8 idx; | |
241 | u32 regdata; | |
242 | ||
243 | idx = brcmf_sdio_chip_getinfidx(ci, coreid); | |
244 | ||
245 | /* if core is already in reset, just return */ | |
79ae3957 FL |
246 | regdata = brcmf_sdio_regrl(sdiodev, |
247 | ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, | |
248 | NULL); | |
086a2e0a FL |
249 | if ((regdata & BCMA_RESET_CTL_RESET) != 0) |
250 | return; | |
251 | ||
e13ce26b | 252 | brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, 0, NULL); |
79ae3957 FL |
253 | regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, |
254 | NULL); | |
086a2e0a FL |
255 | udelay(10); |
256 | ||
e13ce26b FL |
257 | brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, |
258 | BCMA_RESET_CTL_RESET, NULL); | |
086a2e0a FL |
259 | udelay(1); |
260 | } | |
261 | ||
d77e70ff FL |
262 | static void |
263 | brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev, | |
264 | struct chip_info *ci, u16 coreid) | |
2bc78e10 FL |
265 | { |
266 | u32 regdata; | |
086a2e0a FL |
267 | u8 idx; |
268 | ||
269 | idx = brcmf_sdio_chip_getinfidx(ci, coreid); | |
2bc78e10 FL |
270 | |
271 | /* | |
272 | * Must do the disable sequence first to work for | |
273 | * arbitrary current core state. | |
274 | */ | |
d77e70ff | 275 | brcmf_sdio_sb_coredisable(sdiodev, ci, coreid); |
2bc78e10 FL |
276 | |
277 | /* | |
278 | * Now do the initialization sequence. | |
279 | * set reset while enabling the clock and | |
280 | * forcing them on throughout the core | |
281 | */ | |
e13ce26b FL |
282 | brcmf_sdio_regwl(sdiodev, |
283 | CORE_SB(ci->c_inf[idx].base, sbtmstatelow), | |
284 | SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET, | |
285 | NULL); | |
79ae3957 FL |
286 | regdata = brcmf_sdio_regrl(sdiodev, |
287 | CORE_SB(ci->c_inf[idx].base, sbtmstatelow), | |
288 | NULL); | |
2bc78e10 FL |
289 | udelay(1); |
290 | ||
d77e70ff | 291 | /* clear any serror */ |
79ae3957 FL |
292 | regdata = brcmf_sdio_regrl(sdiodev, |
293 | CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), | |
294 | NULL); | |
61213be4 | 295 | if (regdata & SSB_TMSHIGH_SERR) |
e13ce26b FL |
296 | brcmf_sdio_regwl(sdiodev, |
297 | CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), | |
298 | 0, NULL); | |
2bc78e10 | 299 | |
79ae3957 FL |
300 | regdata = brcmf_sdio_regrl(sdiodev, |
301 | CORE_SB(ci->c_inf[idx].base, sbimstate), | |
302 | NULL); | |
61213be4 | 303 | if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) |
e13ce26b FL |
304 | brcmf_sdio_regwl(sdiodev, |
305 | CORE_SB(ci->c_inf[idx].base, sbimstate), | |
306 | regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO), | |
307 | NULL); | |
2bc78e10 FL |
308 | |
309 | /* clear reset and allow it to propagate throughout the core */ | |
e13ce26b FL |
310 | brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), |
311 | SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK, NULL); | |
79ae3957 FL |
312 | regdata = brcmf_sdio_regrl(sdiodev, |
313 | CORE_SB(ci->c_inf[idx].base, sbtmstatelow), | |
314 | NULL); | |
2bc78e10 FL |
315 | udelay(1); |
316 | ||
317 | /* leave clock enabled */ | |
e13ce26b FL |
318 | brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), |
319 | SSB_TMSLOW_CLOCK, NULL); | |
79ae3957 FL |
320 | regdata = brcmf_sdio_regrl(sdiodev, |
321 | CORE_SB(ci->c_inf[idx].base, sbtmstatelow), | |
322 | NULL); | |
d77e70ff FL |
323 | udelay(1); |
324 | } | |
325 | ||
326 | static void | |
327 | brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev, | |
328 | struct chip_info *ci, u16 coreid) | |
329 | { | |
330 | u8 idx; | |
331 | u32 regdata; | |
332 | ||
333 | idx = brcmf_sdio_chip_getinfidx(ci, coreid); | |
334 | ||
335 | /* must disable first to work for arbitrary current core state */ | |
336 | brcmf_sdio_ai_coredisable(sdiodev, ci, coreid); | |
337 | ||
338 | /* now do initialization sequence */ | |
e13ce26b FL |
339 | brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, |
340 | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL); | |
79ae3957 FL |
341 | regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, |
342 | NULL); | |
e13ce26b FL |
343 | brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, |
344 | 0, NULL); | |
d77e70ff FL |
345 | udelay(1); |
346 | ||
e13ce26b FL |
347 | brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, |
348 | BCMA_IOCTL_CLK, NULL); | |
79ae3957 FL |
349 | regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, |
350 | NULL); | |
2bc78e10 FL |
351 | udelay(1); |
352 | } | |
353 | ||
a83369b6 FL |
354 | static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, |
355 | struct chip_info *ci, u32 regs) | |
356 | { | |
357 | u32 regdata; | |
358 | ||
359 | /* | |
360 | * Get CC core rev | |
361 | * Chipid is assume to be at offset 0 from regs arg | |
362 | * For different chiptypes or old sdio hosts w/o chipcommon, | |
363 | * other ways of recognition should be added here. | |
364 | */ | |
99ba15cd FL |
365 | ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON; |
366 | ci->c_inf[0].base = regs; | |
79ae3957 FL |
367 | regdata = brcmf_sdio_regrl(sdiodev, |
368 | CORE_CC_REG(ci->c_inf[0].base, chipid), | |
369 | NULL); | |
a83369b6 FL |
370 | ci->chip = regdata & CID_ID_MASK; |
371 | ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; | |
6ca687d9 | 372 | ci->socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT; |
a83369b6 FL |
373 | |
374 | brcmf_dbg(INFO, "chipid=0x%x chiprev=%d\n", ci->chip, ci->chiprev); | |
375 | ||
376 | /* Address of cores for new chips should be added here */ | |
377 | switch (ci->chip) { | |
4a1c02ce FL |
378 | case BCM43241_CHIP_ID: |
379 | ci->c_inf[0].wrapbase = 0x18100000; | |
380 | ci->c_inf[0].cib = 0x2a084411; | |
381 | ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; | |
382 | ci->c_inf[1].base = 0x18002000; | |
383 | ci->c_inf[1].wrapbase = 0x18102000; | |
384 | ci->c_inf[1].cib = 0x0e004211; | |
385 | ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; | |
386 | ci->c_inf[2].base = 0x18004000; | |
387 | ci->c_inf[2].wrapbase = 0x18104000; | |
388 | ci->c_inf[2].cib = 0x14080401; | |
389 | ci->c_inf[3].id = BCMA_CORE_ARM_CM3; | |
390 | ci->c_inf[3].base = 0x18003000; | |
391 | ci->c_inf[3].wrapbase = 0x18103000; | |
392 | ci->c_inf[3].cib = 0x07004211; | |
393 | ci->ramsize = 0x90000; | |
394 | break; | |
a83369b6 | 395 | case BCM4329_CHIP_ID: |
99ba15cd FL |
396 | ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; |
397 | ci->c_inf[1].base = BCM4329_CORE_BUS_BASE; | |
398 | ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; | |
399 | ci->c_inf[2].base = BCM4329_CORE_SOCRAM_BASE; | |
400 | ci->c_inf[3].id = BCMA_CORE_ARM_CM3; | |
401 | ci->c_inf[3].base = BCM4329_CORE_ARM_BASE; | |
a83369b6 FL |
402 | ci->ramsize = BCM4329_RAMSIZE; |
403 | break; | |
ce2d7d7e FL |
404 | case BCM4330_CHIP_ID: |
405 | ci->c_inf[0].wrapbase = 0x18100000; | |
406 | ci->c_inf[0].cib = 0x27004211; | |
407 | ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; | |
408 | ci->c_inf[1].base = 0x18002000; | |
409 | ci->c_inf[1].wrapbase = 0x18102000; | |
410 | ci->c_inf[1].cib = 0x07004211; | |
411 | ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; | |
412 | ci->c_inf[2].base = 0x18004000; | |
413 | ci->c_inf[2].wrapbase = 0x18104000; | |
414 | ci->c_inf[2].cib = 0x0d080401; | |
415 | ci->c_inf[3].id = BCMA_CORE_ARM_CM3; | |
416 | ci->c_inf[3].base = 0x18003000; | |
417 | ci->c_inf[3].wrapbase = 0x18103000; | |
418 | ci->c_inf[3].cib = 0x03004211; | |
419 | ci->ramsize = 0x48000; | |
420 | break; | |
85a4a1c3 FL |
421 | case BCM4334_CHIP_ID: |
422 | ci->c_inf[0].wrapbase = 0x18100000; | |
423 | ci->c_inf[0].cib = 0x29004211; | |
424 | ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; | |
425 | ci->c_inf[1].base = 0x18002000; | |
426 | ci->c_inf[1].wrapbase = 0x18102000; | |
427 | ci->c_inf[1].cib = 0x0d004211; | |
428 | ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; | |
429 | ci->c_inf[2].base = 0x18004000; | |
430 | ci->c_inf[2].wrapbase = 0x18104000; | |
431 | ci->c_inf[2].cib = 0x13080401; | |
432 | ci->c_inf[3].id = BCMA_CORE_ARM_CM3; | |
433 | ci->c_inf[3].base = 0x18003000; | |
434 | ci->c_inf[3].wrapbase = 0x18103000; | |
435 | ci->c_inf[3].cib = 0x07004211; | |
436 | ci->ramsize = 0x80000; | |
437 | break; | |
a83369b6 | 438 | default: |
5e8149f5 | 439 | brcmf_err("chipid 0x%x is not supported\n", ci->chip); |
a83369b6 FL |
440 | return -ENODEV; |
441 | } | |
442 | ||
6ca687d9 FL |
443 | switch (ci->socitype) { |
444 | case SOCI_SB: | |
445 | ci->iscoreup = brcmf_sdio_sb_iscoreup; | |
523894f2 | 446 | ci->corerev = brcmf_sdio_sb_corerev; |
086a2e0a | 447 | ci->coredisable = brcmf_sdio_sb_coredisable; |
d77e70ff | 448 | ci->resetcore = brcmf_sdio_sb_resetcore; |
6ca687d9 FL |
449 | break; |
450 | case SOCI_AI: | |
451 | ci->iscoreup = brcmf_sdio_ai_iscoreup; | |
523894f2 | 452 | ci->corerev = brcmf_sdio_ai_corerev; |
086a2e0a | 453 | ci->coredisable = brcmf_sdio_ai_coredisable; |
d77e70ff | 454 | ci->resetcore = brcmf_sdio_ai_resetcore; |
6ca687d9 FL |
455 | break; |
456 | default: | |
5e8149f5 | 457 | brcmf_err("socitype %u not supported\n", ci->socitype); |
6ca687d9 FL |
458 | return -ENODEV; |
459 | } | |
460 | ||
a83369b6 FL |
461 | return 0; |
462 | } | |
463 | ||
e63ac6b8 FL |
464 | static int |
465 | brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev) | |
466 | { | |
467 | int err = 0; | |
468 | u8 clkval, clkset; | |
469 | ||
470 | /* Try forcing SDIO core to do ALPAvail request only */ | |
471 | clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; | |
3bba829f | 472 | brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); |
e63ac6b8 | 473 | if (err) { |
5e8149f5 | 474 | brcmf_err("error writing for HT off\n"); |
e63ac6b8 FL |
475 | return err; |
476 | } | |
477 | ||
478 | /* If register supported, wait for ALPAvail and then force ALP */ | |
479 | /* This may take up to 15 milliseconds */ | |
45db339c FL |
480 | clkval = brcmf_sdio_regrb(sdiodev, |
481 | SBSDIO_FUNC1_CHIPCLKCSR, NULL); | |
e63ac6b8 FL |
482 | |
483 | if ((clkval & ~SBSDIO_AVBITS) != clkset) { | |
5e8149f5 | 484 | brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n", |
e63ac6b8 FL |
485 | clkset, clkval); |
486 | return -EACCES; | |
487 | } | |
488 | ||
45db339c FL |
489 | SPINWAIT(((clkval = brcmf_sdio_regrb(sdiodev, |
490 | SBSDIO_FUNC1_CHIPCLKCSR, NULL)), | |
e63ac6b8 FL |
491 | !SBSDIO_ALPAV(clkval)), |
492 | PMU_MAX_TRANSITION_DLY); | |
493 | if (!SBSDIO_ALPAV(clkval)) { | |
5e8149f5 | 494 | brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n", |
e63ac6b8 FL |
495 | clkval); |
496 | return -EBUSY; | |
497 | } | |
498 | ||
499 | clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; | |
3bba829f | 500 | brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); |
e63ac6b8 FL |
501 | udelay(65); |
502 | ||
503 | /* Also, disable the extra SDIO pull-ups */ | |
3bba829f | 504 | brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); |
e63ac6b8 FL |
505 | |
506 | return 0; | |
507 | } | |
508 | ||
5b45e54e FL |
509 | static void |
510 | brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev, | |
511 | struct chip_info *ci) | |
512 | { | |
79ae3957 FL |
513 | u32 base = ci->c_inf[0].base; |
514 | ||
5b45e54e | 515 | /* get chipcommon rev */ |
523894f2 | 516 | ci->c_inf[0].rev = ci->corerev(sdiodev, ci, ci->c_inf[0].id); |
5b45e54e FL |
517 | |
518 | /* get chipcommon capabilites */ | |
79ae3957 FL |
519 | ci->c_inf[0].caps = brcmf_sdio_regrl(sdiodev, |
520 | CORE_CC_REG(base, capabilities), | |
521 | NULL); | |
5b45e54e FL |
522 | |
523 | /* get pmu caps & rev */ | |
99ba15cd | 524 | if (ci->c_inf[0].caps & CC_CAP_PMU) { |
79ae3957 FL |
525 | ci->pmucaps = |
526 | brcmf_sdio_regrl(sdiodev, | |
527 | CORE_CC_REG(base, pmucapabilities), | |
528 | NULL); | |
5b45e54e FL |
529 | ci->pmurev = ci->pmucaps & PCAP_REV_MASK; |
530 | } | |
531 | ||
523894f2 | 532 | ci->c_inf[1].rev = ci->corerev(sdiodev, ci, ci->c_inf[1].id); |
5b45e54e FL |
533 | |
534 | brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n", | |
99ba15cd FL |
535 | ci->c_inf[0].rev, ci->pmurev, |
536 | ci->c_inf[1].rev, ci->c_inf[1].id); | |
966414da FL |
537 | |
538 | /* | |
539 | * Make sure any on-chip ARM is off (in case strapping is wrong), | |
540 | * or downloaded code was already running. | |
541 | */ | |
086a2e0a | 542 | ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3); |
5b45e54e FL |
543 | } |
544 | ||
a83369b6 | 545 | int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, |
a97e4fc5 | 546 | struct chip_info **ci_ptr, u32 regs) |
a83369b6 | 547 | { |
a97e4fc5 FL |
548 | int ret; |
549 | struct chip_info *ci; | |
550 | ||
551 | brcmf_dbg(TRACE, "Enter\n"); | |
552 | ||
553 | /* alloc chip_info_t */ | |
554 | ci = kzalloc(sizeof(struct chip_info), GFP_ATOMIC); | |
555 | if (!ci) | |
556 | return -ENOMEM; | |
a83369b6 | 557 | |
e63ac6b8 FL |
558 | ret = brcmf_sdio_chip_buscoreprep(sdiodev); |
559 | if (ret != 0) | |
a97e4fc5 | 560 | goto err; |
e63ac6b8 | 561 | |
a83369b6 FL |
562 | ret = brcmf_sdio_chip_recognition(sdiodev, ci, regs); |
563 | if (ret != 0) | |
a97e4fc5 | 564 | goto err; |
a83369b6 | 565 | |
5b45e54e FL |
566 | brcmf_sdio_chip_buscoresetup(sdiodev, ci); |
567 | ||
e13ce26b FL |
568 | brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopullup), |
569 | 0, NULL); | |
570 | brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopulldown), | |
571 | 0, NULL); | |
960908dc | 572 | |
a97e4fc5 FL |
573 | *ci_ptr = ci; |
574 | return 0; | |
575 | ||
576 | err: | |
577 | kfree(ci); | |
a83369b6 FL |
578 | return ret; |
579 | } | |
a8a6c045 FL |
580 | |
581 | void | |
582 | brcmf_sdio_chip_detach(struct chip_info **ci_ptr) | |
583 | { | |
584 | brcmf_dbg(TRACE, "Enter\n"); | |
585 | ||
586 | kfree(*ci_ptr); | |
587 | *ci_ptr = NULL; | |
588 | } | |
e12afb6c FL |
589 | |
590 | static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len) | |
591 | { | |
592 | const char *fmt; | |
593 | ||
594 | fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; | |
595 | snprintf(buf, len, fmt, chipid); | |
596 | return buf; | |
597 | } | |
598 | ||
599 | void | |
600 | brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, | |
601 | struct chip_info *ci, u32 drivestrength) | |
602 | { | |
603 | struct sdiod_drive_str *str_tab = NULL; | |
604 | u32 str_mask = 0; | |
605 | u32 str_shift = 0; | |
606 | char chn[8]; | |
79ae3957 | 607 | u32 base = ci->c_inf[0].base; |
e12afb6c | 608 | |
99ba15cd | 609 | if (!(ci->c_inf[0].caps & CC_CAP_PMU)) |
e12afb6c FL |
610 | return; |
611 | ||
612 | switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { | |
ce2d7d7e | 613 | case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12): |
ffb27565 | 614 | str_tab = (struct sdiod_drive_str *)&sdiod_drvstr_tab1_1v8; |
ce2d7d7e FL |
615 | str_mask = 0x00003800; |
616 | str_shift = 11; | |
617 | break; | |
e12afb6c | 618 | default: |
5e8149f5 | 619 | brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", |
e12afb6c FL |
620 | brcmf_sdio_chip_name(ci->chip, chn, 8), |
621 | ci->chiprev, ci->pmurev); | |
622 | break; | |
623 | } | |
624 | ||
625 | if (str_tab != NULL) { | |
626 | u32 drivestrength_sel = 0; | |
627 | u32 cc_data_temp; | |
628 | int i; | |
629 | ||
630 | for (i = 0; str_tab[i].strength != 0; i++) { | |
631 | if (drivestrength >= str_tab[i].strength) { | |
632 | drivestrength_sel = str_tab[i].sel; | |
633 | break; | |
634 | } | |
635 | } | |
636 | ||
e13ce26b FL |
637 | brcmf_sdio_regwl(sdiodev, CORE_CC_REG(base, chipcontrol_addr), |
638 | 1, NULL); | |
79ae3957 FL |
639 | cc_data_temp = |
640 | brcmf_sdio_regrl(sdiodev, | |
641 | CORE_CC_REG(base, chipcontrol_addr), | |
642 | NULL); | |
e12afb6c FL |
643 | cc_data_temp &= ~str_mask; |
644 | drivestrength_sel <<= str_shift; | |
645 | cc_data_temp |= drivestrength_sel; | |
e13ce26b FL |
646 | brcmf_sdio_regwl(sdiodev, CORE_CC_REG(base, chipcontrol_addr), |
647 | cc_data_temp, NULL); | |
e12afb6c FL |
648 | |
649 | brcmf_dbg(INFO, "SDIO: %dmA drive strength selected, set to 0x%08x\n", | |
650 | drivestrength, cc_data_temp); | |
651 | } | |
652 | } |