blob: 3975b5aa1d395a235f29f7c2732e825a97c514f5 [file] [log] [blame]
Franky Lina83369b2011-11-04 22:23:28 +01001/*
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>
Franky Lin61213be2011-11-04 22:23:41 +010021#include <linux/ssb/ssb_regs.h>
Franky Lin99ba15c2011-11-04 22:23:42 +010022#include <linux/bcma/bcma.h>
Franky Lin61213be2011-11-04 22:23:41 +010023
Franky Lina83369b2011-11-04 22:23:28 +010024#include <chipcommon.h>
25#include <brcm_hw_ids.h>
26#include <brcmu_wifi.h>
27#include <brcmu_utils.h>
Franky Lin2d4a9af2011-11-04 22:23:31 +010028#include <soc.h>
Franky Lina83369b2011-11-04 22:23:28 +010029#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
Franky Lina83369b2011-11-04 22:23:28 +010043#define SBCOREREV(sbidh) \
Franky Lin61213be2011-11-04 22:23:41 +010044 ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \
45 ((sbidh) & SSB_IDHIGH_RCLO))
Franky Lina83369b2011-11-04 22:23:28 +010046
Franky Lin6ca687d2011-11-10 20:30:21 +010047/* SOC Interconnect types (aka chip types) */
48#define SOCI_SB 0
49#define SOCI_AI 1
50
Franky Lin523894f2011-11-10 20:30:22 +010051/* EROM CompIdentB */
52#define CIB_REV_MASK 0xff000000
53#define CIB_REV_SHIFT 24
54
Franky Lin1640f282013-04-11 13:28:51 +020055/* ARM CR4 core specific control flag bits */
56#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020
57
Franky Line12afb62011-11-04 22:23:40 +010058#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu))
59/* SDIO Pad drive strength to select value mappings */
60struct sdiod_drive_str {
61 u8 strength; /* Pad Drive Strength in mA */
62 u8 sel; /* Chip-specific select value */
63};
Franky Lince2d7d72011-12-08 15:06:39 -080064/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */
Franky Linffb27562011-12-08 15:06:40 -080065static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = {
Franky Lince2d7d72011-12-08 15:06:39 -080066 {32, 0x6},
67 {26, 0x7},
68 {22, 0x4},
69 {16, 0x5},
70 {12, 0x2},
71 {8, 0x3},
72 {4, 0x0},
73 {0, 0x1}
74};
75
Franky Lin99ba15c2011-11-04 22:23:42 +010076u8
77brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid)
78{
79 u8 idx;
80
81 for (idx = 0; idx < BRCMF_MAX_CORENUM; idx++)
82 if (coreid == ci->c_inf[idx].id)
83 return idx;
84
85 return BRCMF_MAX_CORENUM;
86}
87
Franky Lin454d2a82011-11-04 22:23:37 +010088static u32
Franky Lin523894f2011-11-10 20:30:22 +010089brcmf_sdio_sb_corerev(struct brcmf_sdio_dev *sdiodev,
90 struct chip_info *ci, u16 coreid)
Franky Lin454d2a82011-11-04 22:23:37 +010091{
92 u32 regdata;
Franky Lin523894f2011-11-10 20:30:22 +010093 u8 idx;
94
95 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
Franky Lin454d2a82011-11-04 22:23:37 +010096
Franky Lin79ae3952012-05-04 18:27:34 -070097 regdata = brcmf_sdio_regrl(sdiodev,
98 CORE_SB(ci->c_inf[idx].base, sbidhigh),
99 NULL);
Franky Lin454d2a82011-11-04 22:23:37 +0100100 return SBCOREREV(regdata);
101}
102
Franky Lin523894f2011-11-10 20:30:22 +0100103static u32
104brcmf_sdio_ai_corerev(struct brcmf_sdio_dev *sdiodev,
105 struct chip_info *ci, u16 coreid)
106{
107 u8 idx;
108
109 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
110
111 return (ci->c_inf[idx].cib & CIB_REV_MASK) >> CIB_REV_SHIFT;
112}
113
Franky Lin6ca687d2011-11-10 20:30:21 +0100114static bool
115brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev,
116 struct chip_info *ci, u16 coreid)
Franky Lind8f64a42011-11-04 22:23:36 +0100117{
118 u32 regdata;
Franky Lin6ca687d2011-11-10 20:30:21 +0100119 u8 idx;
120
121 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
Franky Lind8f64a42011-11-04 22:23:36 +0100122
Franky Lin79ae3952012-05-04 18:27:34 -0700123 regdata = brcmf_sdio_regrl(sdiodev,
124 CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
125 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100126 regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT |
127 SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK);
128 return (SSB_TMSLOW_CLOCK == regdata);
Franky Lind8f64a42011-11-04 22:23:36 +0100129}
130
Franky Lin6ca687d2011-11-10 20:30:21 +0100131static bool
132brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev,
133 struct chip_info *ci, u16 coreid)
134{
135 u32 regdata;
136 u8 idx;
137 bool ret;
138
139 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
140
Franky Lin79ae3952012-05-04 18:27:34 -0700141 regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
142 NULL);
Franky Lin6ca687d2011-11-10 20:30:21 +0100143 ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK;
144
Franky Lin79ae3952012-05-04 18:27:34 -0700145 regdata = brcmf_sdio_regrl(sdiodev,
146 ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
147 NULL);
Franky Lin6ca687d2011-11-10 20:30:21 +0100148 ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0);
149
150 return ret;
151}
152
Franky Lin086a2e02011-11-10 20:30:23 +0100153static void
154brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev,
Franky Lin1640f282013-04-11 13:28:51 +0200155 struct chip_info *ci, u16 coreid, u32 core_bits)
Franky Lin2d4a9af2011-11-04 22:23:31 +0100156{
Franky Lin79ae3952012-05-04 18:27:34 -0700157 u32 regdata, base;
Franky Lin086a2e02011-11-10 20:30:23 +0100158 u8 idx;
159
160 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
Franky Lin79ae3952012-05-04 18:27:34 -0700161 base = ci->c_inf[idx].base;
Franky Lin2d4a9af2011-11-04 22:23:31 +0100162
Franky Lin79ae3952012-05-04 18:27:34 -0700163 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100164 if (regdata & SSB_TMSLOW_RESET)
Franky Lin2d4a9af2011-11-04 22:23:31 +0100165 return;
166
Franky Lin79ae3952012-05-04 18:27:34 -0700167 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100168 if ((regdata & SSB_TMSLOW_CLOCK) != 0) {
Franky Lin2d4a9af2011-11-04 22:23:31 +0100169 /*
170 * set target reject and spin until busy is clear
171 * (preserve core-specific bits)
172 */
Franky Lin79ae3952012-05-04 18:27:34 -0700173 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow),
174 NULL);
Franky Line13ce262012-05-04 18:27:35 -0700175 brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
176 regdata | SSB_TMSLOW_REJECT, NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100177
Franky Lin79ae3952012-05-04 18:27:34 -0700178 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow),
179 NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100180 udelay(1);
Franky Lin79ae3952012-05-04 18:27:34 -0700181 SPINWAIT((brcmf_sdio_regrl(sdiodev,
182 CORE_SB(base, sbtmstatehigh),
183 NULL) &
Franky Lin61213be2011-11-04 22:23:41 +0100184 SSB_TMSHIGH_BUSY), 100000);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100185
Franky Lin79ae3952012-05-04 18:27:34 -0700186 regdata = brcmf_sdio_regrl(sdiodev,
187 CORE_SB(base, sbtmstatehigh),
188 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100189 if (regdata & SSB_TMSHIGH_BUSY)
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100190 brcmf_err("core state still busy\n");
Franky Lin2d4a9af2011-11-04 22:23:31 +0100191
Franky Lin79ae3952012-05-04 18:27:34 -0700192 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow),
193 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100194 if (regdata & SSB_IDLOW_INITIATOR) {
Franky Lin79ae3952012-05-04 18:27:34 -0700195 regdata = brcmf_sdio_regrl(sdiodev,
196 CORE_SB(base, sbimstate),
197 NULL);
198 regdata |= SSB_IMSTATE_REJECT;
Franky Line13ce262012-05-04 18:27:35 -0700199 brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate),
200 regdata, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700201 regdata = brcmf_sdio_regrl(sdiodev,
202 CORE_SB(base, sbimstate),
203 NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100204 udelay(1);
Franky Lin79ae3952012-05-04 18:27:34 -0700205 SPINWAIT((brcmf_sdio_regrl(sdiodev,
206 CORE_SB(base, sbimstate),
207 NULL) &
Franky Lin61213be2011-11-04 22:23:41 +0100208 SSB_IMSTATE_BUSY), 100000);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100209 }
210
211 /* set reset and reject while enabling the clocks */
Franky Line13ce262012-05-04 18:27:35 -0700212 regdata = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
213 SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET;
214 brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
215 regdata, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700216 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow),
217 NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100218 udelay(10);
219
220 /* clear the initiator reject bit */
Franky Lin79ae3952012-05-04 18:27:34 -0700221 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow),
222 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100223 if (regdata & SSB_IDLOW_INITIATOR) {
Franky Lin79ae3952012-05-04 18:27:34 -0700224 regdata = brcmf_sdio_regrl(sdiodev,
225 CORE_SB(base, sbimstate),
226 NULL);
227 regdata &= ~SSB_IMSTATE_REJECT;
Franky Line13ce262012-05-04 18:27:35 -0700228 brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate),
229 regdata, NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100230 }
231 }
232
233 /* leave reset and reject asserted */
Franky Line13ce262012-05-04 18:27:35 -0700234 brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
235 (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET), NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100236 udelay(1);
237}
238
Franky Lin086a2e02011-11-10 20:30:23 +0100239static void
240brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev,
Franky Lin1640f282013-04-11 13:28:51 +0200241 struct chip_info *ci, u16 coreid, u32 core_bits)
Franky Lin086a2e02011-11-10 20:30:23 +0100242{
243 u8 idx;
244 u32 regdata;
245
246 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
247
248 /* if core is already in reset, just return */
Franky Lin79ae3952012-05-04 18:27:34 -0700249 regdata = brcmf_sdio_regrl(sdiodev,
250 ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
251 NULL);
Franky Lin086a2e02011-11-10 20:30:23 +0100252 if ((regdata & BCMA_RESET_CTL_RESET) != 0)
253 return;
254
Franky Lin1640f282013-04-11 13:28:51 +0200255 /* ensure no pending backplane operation
256 * 300uc should be sufficient for backplane ops to be finish
257 * extra 10ms is taken into account for firmware load stage
258 * after 10300us carry on disabling the core anyway
259 */
260 SPINWAIT(brcmf_sdio_regrl(sdiodev,
261 ci->c_inf[idx].wrapbase+BCMA_RESET_ST,
262 NULL), 10300);
263 regdata = brcmf_sdio_regrl(sdiodev,
264 ci->c_inf[idx].wrapbase+BCMA_RESET_ST,
Franky Lin79ae3952012-05-04 18:27:34 -0700265 NULL);
Franky Lin1640f282013-04-11 13:28:51 +0200266 if (regdata)
267 brcmf_err("disabling core 0x%x with reset status %x\n",
268 coreid, regdata);
Franky Lin086a2e02011-11-10 20:30:23 +0100269
Franky Line13ce262012-05-04 18:27:35 -0700270 brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
271 BCMA_RESET_CTL_RESET, NULL);
Franky Lin086a2e02011-11-10 20:30:23 +0100272 udelay(1);
Franky Lin1640f282013-04-11 13:28:51 +0200273
274 brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
275 core_bits, NULL);
276 regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
277 NULL);
278 usleep_range(10, 20);
279
Franky Lin086a2e02011-11-10 20:30:23 +0100280}
281
Franky Lind77e70f2011-11-10 20:30:24 +0100282static void
283brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev,
Franky Lin1640f282013-04-11 13:28:51 +0200284 struct chip_info *ci, u16 coreid, u32 core_bits)
Franky Lin2bc78e12011-11-04 22:23:38 +0100285{
286 u32 regdata;
Franky Lin086a2e02011-11-10 20:30:23 +0100287 u8 idx;
288
289 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
Franky Lin2bc78e12011-11-04 22:23:38 +0100290
291 /*
292 * Must do the disable sequence first to work for
293 * arbitrary current core state.
294 */
Franky Lin1640f282013-04-11 13:28:51 +0200295 brcmf_sdio_sb_coredisable(sdiodev, ci, coreid, 0);
Franky Lin2bc78e12011-11-04 22:23:38 +0100296
297 /*
298 * Now do the initialization sequence.
299 * set reset while enabling the clock and
300 * forcing them on throughout the core
301 */
Franky Line13ce262012-05-04 18:27:35 -0700302 brcmf_sdio_regwl(sdiodev,
303 CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
304 SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET,
305 NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700306 regdata = brcmf_sdio_regrl(sdiodev,
307 CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
308 NULL);
Franky Lin2bc78e12011-11-04 22:23:38 +0100309 udelay(1);
310
Franky Lind77e70f2011-11-10 20:30:24 +0100311 /* clear any serror */
Franky Lin79ae3952012-05-04 18:27:34 -0700312 regdata = brcmf_sdio_regrl(sdiodev,
313 CORE_SB(ci->c_inf[idx].base, sbtmstatehigh),
314 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100315 if (regdata & SSB_TMSHIGH_SERR)
Franky Line13ce262012-05-04 18:27:35 -0700316 brcmf_sdio_regwl(sdiodev,
317 CORE_SB(ci->c_inf[idx].base, sbtmstatehigh),
318 0, NULL);
Franky Lin2bc78e12011-11-04 22:23:38 +0100319
Franky Lin79ae3952012-05-04 18:27:34 -0700320 regdata = brcmf_sdio_regrl(sdiodev,
321 CORE_SB(ci->c_inf[idx].base, sbimstate),
322 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100323 if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO))
Franky Line13ce262012-05-04 18:27:35 -0700324 brcmf_sdio_regwl(sdiodev,
325 CORE_SB(ci->c_inf[idx].base, sbimstate),
326 regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO),
327 NULL);
Franky Lin2bc78e12011-11-04 22:23:38 +0100328
329 /* clear reset and allow it to propagate throughout the core */
Franky Line13ce262012-05-04 18:27:35 -0700330 brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
331 SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700332 regdata = brcmf_sdio_regrl(sdiodev,
333 CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
334 NULL);
Franky Lin2bc78e12011-11-04 22:23:38 +0100335 udelay(1);
336
337 /* leave clock enabled */
Franky Line13ce262012-05-04 18:27:35 -0700338 brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
339 SSB_TMSLOW_CLOCK, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700340 regdata = brcmf_sdio_regrl(sdiodev,
341 CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
342 NULL);
Franky Lind77e70f2011-11-10 20:30:24 +0100343 udelay(1);
344}
345
346static void
347brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev,
Franky Lin1640f282013-04-11 13:28:51 +0200348 struct chip_info *ci, u16 coreid, u32 core_bits)
Franky Lind77e70f2011-11-10 20:30:24 +0100349{
350 u8 idx;
351 u32 regdata;
352
353 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
354
355 /* must disable first to work for arbitrary current core state */
Franky Lin1640f282013-04-11 13:28:51 +0200356 brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, core_bits);
Franky Lind77e70f2011-11-10 20:30:24 +0100357
358 /* now do initialization sequence */
Franky Line13ce262012-05-04 18:27:35 -0700359 brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
Franky Lin1640f282013-04-11 13:28:51 +0200360 core_bits | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700361 regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
362 NULL);
Franky Line13ce262012-05-04 18:27:35 -0700363 brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
364 0, NULL);
Franky Lin1640f282013-04-11 13:28:51 +0200365 regdata = brcmf_sdio_regrl(sdiodev,
366 ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
367 NULL);
Franky Lind77e70f2011-11-10 20:30:24 +0100368 udelay(1);
369
Franky Line13ce262012-05-04 18:27:35 -0700370 brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
Franky Lin1640f282013-04-11 13:28:51 +0200371 core_bits | BCMA_IOCTL_CLK, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700372 regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
373 NULL);
Franky Lin2bc78e12011-11-04 22:23:38 +0100374 udelay(1);
375}
376
Franky Lin1640f282013-04-11 13:28:51 +0200377#ifdef DEBUG
378/* safety check for chipinfo */
379static int brcmf_sdio_chip_cichk(struct chip_info *ci)
380{
381 u8 core_idx;
382
383 /* check RAM core presence for ARM CM3 core */
384 core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
385 if (BRCMF_MAX_CORENUM != core_idx) {
386 core_idx = brcmf_sdio_chip_getinfidx(ci,
387 BCMA_CORE_INTERNAL_MEM);
388 if (BRCMF_MAX_CORENUM == core_idx) {
389 brcmf_err("RAM core not provided with ARM CM3 core\n");
390 return -ENODEV;
391 }
392 }
393
394 /* check RAM base for ARM CR4 core */
395 core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CR4);
396 if (BRCMF_MAX_CORENUM != core_idx) {
397 if (ci->rambase == 0) {
398 brcmf_err("RAM base not provided with ARM CR4 core\n");
399 return -ENOMEM;
400 }
401 }
402
403 return 0;
404}
405#else /* DEBUG */
406static inline int brcmf_sdio_chip_cichk(struct chip_info *ci)
407{
408 return 0;
409}
410#endif
411
Franky Lina83369b2011-11-04 22:23:28 +0100412static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev,
413 struct chip_info *ci, u32 regs)
414{
415 u32 regdata;
Franky Lin1640f282013-04-11 13:28:51 +0200416 int ret;
Franky Lina83369b2011-11-04 22:23:28 +0100417
Franky Lin069eddd2013-04-11 13:28:48 +0200418 /* Get CC core rev
Franky Lina83369b2011-11-04 22:23:28 +0100419 * Chipid is assume to be at offset 0 from regs arg
420 * For different chiptypes or old sdio hosts w/o chipcommon,
421 * other ways of recognition should be added here.
422 */
Franky Lin99ba15c2011-11-04 22:23:42 +0100423 ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON;
424 ci->c_inf[0].base = regs;
Franky Lin79ae3952012-05-04 18:27:34 -0700425 regdata = brcmf_sdio_regrl(sdiodev,
426 CORE_CC_REG(ci->c_inf[0].base, chipid),
427 NULL);
Franky Lina83369b2011-11-04 22:23:28 +0100428 ci->chip = regdata & CID_ID_MASK;
429 ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
Franky Lin6ca687d2011-11-10 20:30:21 +0100430 ci->socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT;
Franky Lina83369b2011-11-04 22:23:28 +0100431
432 brcmf_dbg(INFO, "chipid=0x%x chiprev=%d\n", ci->chip, ci->chiprev);
433
434 /* Address of cores for new chips should be added here */
435 switch (ci->chip) {
Franky Lin4a1c02c2012-08-30 19:42:59 +0200436 case BCM43241_CHIP_ID:
437 ci->c_inf[0].wrapbase = 0x18100000;
438 ci->c_inf[0].cib = 0x2a084411;
439 ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
440 ci->c_inf[1].base = 0x18002000;
441 ci->c_inf[1].wrapbase = 0x18102000;
442 ci->c_inf[1].cib = 0x0e004211;
443 ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
444 ci->c_inf[2].base = 0x18004000;
445 ci->c_inf[2].wrapbase = 0x18104000;
446 ci->c_inf[2].cib = 0x14080401;
447 ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
448 ci->c_inf[3].base = 0x18003000;
449 ci->c_inf[3].wrapbase = 0x18103000;
450 ci->c_inf[3].cib = 0x07004211;
451 ci->ramsize = 0x90000;
452 break;
Franky Lina83369b2011-11-04 22:23:28 +0100453 case BCM4329_CHIP_ID:
Franky Lin99ba15c2011-11-04 22:23:42 +0100454 ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
455 ci->c_inf[1].base = BCM4329_CORE_BUS_BASE;
456 ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
457 ci->c_inf[2].base = BCM4329_CORE_SOCRAM_BASE;
458 ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
459 ci->c_inf[3].base = BCM4329_CORE_ARM_BASE;
Franky Lina83369b2011-11-04 22:23:28 +0100460 ci->ramsize = BCM4329_RAMSIZE;
461 break;
Franky Lince2d7d72011-12-08 15:06:39 -0800462 case BCM4330_CHIP_ID:
463 ci->c_inf[0].wrapbase = 0x18100000;
464 ci->c_inf[0].cib = 0x27004211;
465 ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
466 ci->c_inf[1].base = 0x18002000;
467 ci->c_inf[1].wrapbase = 0x18102000;
468 ci->c_inf[1].cib = 0x07004211;
469 ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
470 ci->c_inf[2].base = 0x18004000;
471 ci->c_inf[2].wrapbase = 0x18104000;
472 ci->c_inf[2].cib = 0x0d080401;
473 ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
474 ci->c_inf[3].base = 0x18003000;
475 ci->c_inf[3].wrapbase = 0x18103000;
476 ci->c_inf[3].cib = 0x03004211;
477 ci->ramsize = 0x48000;
478 break;
Franky Lin85a4a1c2012-06-26 21:26:39 +0200479 case BCM4334_CHIP_ID:
480 ci->c_inf[0].wrapbase = 0x18100000;
481 ci->c_inf[0].cib = 0x29004211;
482 ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
483 ci->c_inf[1].base = 0x18002000;
484 ci->c_inf[1].wrapbase = 0x18102000;
485 ci->c_inf[1].cib = 0x0d004211;
486 ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
487 ci->c_inf[2].base = 0x18004000;
488 ci->c_inf[2].wrapbase = 0x18104000;
489 ci->c_inf[2].cib = 0x13080401;
490 ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
491 ci->c_inf[3].base = 0x18003000;
492 ci->c_inf[3].wrapbase = 0x18103000;
493 ci->c_inf[3].cib = 0x07004211;
494 ci->ramsize = 0x80000;
495 break;
Franky Lin6a1c7482013-04-11 13:28:53 +0200496 case BCM4335_CHIP_ID:
497 ci->c_inf[0].wrapbase = 0x18100000;
498 ci->c_inf[0].cib = 0x2b084411;
499 ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
500 ci->c_inf[1].base = 0x18005000;
501 ci->c_inf[1].wrapbase = 0x18105000;
502 ci->c_inf[1].cib = 0x0f004211;
503 ci->c_inf[2].id = BCMA_CORE_ARM_CR4;
504 ci->c_inf[2].base = 0x18002000;
505 ci->c_inf[2].wrapbase = 0x18102000;
506 ci->c_inf[2].cib = 0x01084411;
507 ci->ramsize = 0xc0000;
508 ci->rambase = 0x180000;
509 break;
Franky Lina83369b2011-11-04 22:23:28 +0100510 default:
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100511 brcmf_err("chipid 0x%x is not supported\n", ci->chip);
Franky Lina83369b2011-11-04 22:23:28 +0100512 return -ENODEV;
513 }
514
Franky Lin1640f282013-04-11 13:28:51 +0200515 ret = brcmf_sdio_chip_cichk(ci);
516 if (ret)
517 return ret;
518
Franky Lin6ca687d2011-11-10 20:30:21 +0100519 switch (ci->socitype) {
520 case SOCI_SB:
521 ci->iscoreup = brcmf_sdio_sb_iscoreup;
Franky Lin523894f2011-11-10 20:30:22 +0100522 ci->corerev = brcmf_sdio_sb_corerev;
Franky Lin086a2e02011-11-10 20:30:23 +0100523 ci->coredisable = brcmf_sdio_sb_coredisable;
Franky Lind77e70f2011-11-10 20:30:24 +0100524 ci->resetcore = brcmf_sdio_sb_resetcore;
Franky Lin6ca687d2011-11-10 20:30:21 +0100525 break;
526 case SOCI_AI:
527 ci->iscoreup = brcmf_sdio_ai_iscoreup;
Franky Lin523894f2011-11-10 20:30:22 +0100528 ci->corerev = brcmf_sdio_ai_corerev;
Franky Lin086a2e02011-11-10 20:30:23 +0100529 ci->coredisable = brcmf_sdio_ai_coredisable;
Franky Lind77e70f2011-11-10 20:30:24 +0100530 ci->resetcore = brcmf_sdio_ai_resetcore;
Franky Lin6ca687d2011-11-10 20:30:21 +0100531 break;
532 default:
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100533 brcmf_err("socitype %u not supported\n", ci->socitype);
Franky Lin6ca687d2011-11-10 20:30:21 +0100534 return -ENODEV;
535 }
536
Franky Lina83369b2011-11-04 22:23:28 +0100537 return 0;
538}
539
Franky Line63ac6b2011-11-04 22:23:29 +0100540static int
541brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev)
542{
543 int err = 0;
544 u8 clkval, clkset;
545
546 /* Try forcing SDIO core to do ALPAvail request only */
547 clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
Franky Lin3bba8292012-05-04 18:27:33 -0700548 brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
Franky Line63ac6b2011-11-04 22:23:29 +0100549 if (err) {
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100550 brcmf_err("error writing for HT off\n");
Franky Line63ac6b2011-11-04 22:23:29 +0100551 return err;
552 }
553
554 /* If register supported, wait for ALPAvail and then force ALP */
555 /* This may take up to 15 milliseconds */
Franky Lin45db3392012-05-04 18:27:32 -0700556 clkval = brcmf_sdio_regrb(sdiodev,
557 SBSDIO_FUNC1_CHIPCLKCSR, NULL);
Franky Line63ac6b2011-11-04 22:23:29 +0100558
559 if ((clkval & ~SBSDIO_AVBITS) != clkset) {
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100560 brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
Franky Line63ac6b2011-11-04 22:23:29 +0100561 clkset, clkval);
562 return -EACCES;
563 }
564
Franky Lin45db3392012-05-04 18:27:32 -0700565 SPINWAIT(((clkval = brcmf_sdio_regrb(sdiodev,
566 SBSDIO_FUNC1_CHIPCLKCSR, NULL)),
Franky Line63ac6b2011-11-04 22:23:29 +0100567 !SBSDIO_ALPAV(clkval)),
568 PMU_MAX_TRANSITION_DLY);
569 if (!SBSDIO_ALPAV(clkval)) {
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100570 brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n",
Franky Line63ac6b2011-11-04 22:23:29 +0100571 clkval);
572 return -EBUSY;
573 }
574
575 clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP;
Franky Lin3bba8292012-05-04 18:27:33 -0700576 brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
Franky Line63ac6b2011-11-04 22:23:29 +0100577 udelay(65);
578
579 /* Also, disable the extra SDIO pull-ups */
Franky Lin3bba8292012-05-04 18:27:33 -0700580 brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
Franky Line63ac6b2011-11-04 22:23:29 +0100581
582 return 0;
583}
584
Franky Lin5b45e542011-11-04 22:23:30 +0100585static void
586brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev,
587 struct chip_info *ci)
588{
Franky Lin79ae3952012-05-04 18:27:34 -0700589 u32 base = ci->c_inf[0].base;
590
Franky Lin5b45e542011-11-04 22:23:30 +0100591 /* get chipcommon rev */
Franky Lin523894f2011-11-10 20:30:22 +0100592 ci->c_inf[0].rev = ci->corerev(sdiodev, ci, ci->c_inf[0].id);
Franky Lin5b45e542011-11-04 22:23:30 +0100593
594 /* get chipcommon capabilites */
Franky Lin79ae3952012-05-04 18:27:34 -0700595 ci->c_inf[0].caps = brcmf_sdio_regrl(sdiodev,
596 CORE_CC_REG(base, capabilities),
597 NULL);
Franky Lin5b45e542011-11-04 22:23:30 +0100598
599 /* get pmu caps & rev */
Franky Lin99ba15c2011-11-04 22:23:42 +0100600 if (ci->c_inf[0].caps & CC_CAP_PMU) {
Franky Lin79ae3952012-05-04 18:27:34 -0700601 ci->pmucaps =
602 brcmf_sdio_regrl(sdiodev,
603 CORE_CC_REG(base, pmucapabilities),
604 NULL);
Franky Lin5b45e542011-11-04 22:23:30 +0100605 ci->pmurev = ci->pmucaps & PCAP_REV_MASK;
606 }
607
Franky Lin523894f2011-11-10 20:30:22 +0100608 ci->c_inf[1].rev = ci->corerev(sdiodev, ci, ci->c_inf[1].id);
Franky Lin5b45e542011-11-04 22:23:30 +0100609
610 brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n",
Franky Lin99ba15c2011-11-04 22:23:42 +0100611 ci->c_inf[0].rev, ci->pmurev,
612 ci->c_inf[1].rev, ci->c_inf[1].id);
Franky Lin966414d2011-11-04 22:23:32 +0100613
614 /*
615 * Make sure any on-chip ARM is off (in case strapping is wrong),
616 * or downloaded code was already running.
617 */
Franky Lin1640f282013-04-11 13:28:51 +0200618 ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0);
Franky Lin5b45e542011-11-04 22:23:30 +0100619}
620
Franky Lina83369b2011-11-04 22:23:28 +0100621int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev,
Franky Lina97e4fc2011-11-04 22:23:35 +0100622 struct chip_info **ci_ptr, u32 regs)
Franky Lina83369b2011-11-04 22:23:28 +0100623{
Franky Lina97e4fc2011-11-04 22:23:35 +0100624 int ret;
625 struct chip_info *ci;
626
627 brcmf_dbg(TRACE, "Enter\n");
628
629 /* alloc chip_info_t */
630 ci = kzalloc(sizeof(struct chip_info), GFP_ATOMIC);
631 if (!ci)
632 return -ENOMEM;
Franky Lina83369b2011-11-04 22:23:28 +0100633
Franky Line63ac6b2011-11-04 22:23:29 +0100634 ret = brcmf_sdio_chip_buscoreprep(sdiodev);
635 if (ret != 0)
Franky Lina97e4fc2011-11-04 22:23:35 +0100636 goto err;
Franky Line63ac6b2011-11-04 22:23:29 +0100637
Franky Lina83369b2011-11-04 22:23:28 +0100638 ret = brcmf_sdio_chip_recognition(sdiodev, ci, regs);
639 if (ret != 0)
Franky Lina97e4fc2011-11-04 22:23:35 +0100640 goto err;
Franky Lina83369b2011-11-04 22:23:28 +0100641
Franky Lin5b45e542011-11-04 22:23:30 +0100642 brcmf_sdio_chip_buscoresetup(sdiodev, ci);
643
Franky Line13ce262012-05-04 18:27:35 -0700644 brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopullup),
645 0, NULL);
646 brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopulldown),
647 0, NULL);
Franky Lin960908d2011-11-04 22:23:33 +0100648
Franky Lina97e4fc2011-11-04 22:23:35 +0100649 *ci_ptr = ci;
650 return 0;
651
652err:
653 kfree(ci);
Franky Lina83369b2011-11-04 22:23:28 +0100654 return ret;
655}
Franky Lina8a6c042011-11-04 22:23:39 +0100656
657void
658brcmf_sdio_chip_detach(struct chip_info **ci_ptr)
659{
660 brcmf_dbg(TRACE, "Enter\n");
661
662 kfree(*ci_ptr);
663 *ci_ptr = NULL;
664}
Franky Line12afb62011-11-04 22:23:40 +0100665
666static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len)
667{
668 const char *fmt;
669
670 fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
671 snprintf(buf, len, fmt, chipid);
672 return buf;
673}
674
675void
676brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
677 struct chip_info *ci, u32 drivestrength)
678{
679 struct sdiod_drive_str *str_tab = NULL;
680 u32 str_mask = 0;
681 u32 str_shift = 0;
682 char chn[8];
Franky Lin79ae3952012-05-04 18:27:34 -0700683 u32 base = ci->c_inf[0].base;
Franky Line12afb62011-11-04 22:23:40 +0100684
Franky Lin99ba15c2011-11-04 22:23:42 +0100685 if (!(ci->c_inf[0].caps & CC_CAP_PMU))
Franky Line12afb62011-11-04 22:23:40 +0100686 return;
687
688 switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) {
Franky Lince2d7d72011-12-08 15:06:39 -0800689 case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12):
Franky Linffb27562011-12-08 15:06:40 -0800690 str_tab = (struct sdiod_drive_str *)&sdiod_drvstr_tab1_1v8;
Franky Lince2d7d72011-12-08 15:06:39 -0800691 str_mask = 0x00003800;
692 str_shift = 11;
693 break;
Franky Line12afb62011-11-04 22:23:40 +0100694 default:
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100695 brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n",
Franky Line12afb62011-11-04 22:23:40 +0100696 brcmf_sdio_chip_name(ci->chip, chn, 8),
697 ci->chiprev, ci->pmurev);
698 break;
699 }
700
701 if (str_tab != NULL) {
702 u32 drivestrength_sel = 0;
703 u32 cc_data_temp;
704 int i;
705
706 for (i = 0; str_tab[i].strength != 0; i++) {
707 if (drivestrength >= str_tab[i].strength) {
708 drivestrength_sel = str_tab[i].sel;
709 break;
710 }
711 }
712
Franky Line13ce262012-05-04 18:27:35 -0700713 brcmf_sdio_regwl(sdiodev, CORE_CC_REG(base, chipcontrol_addr),
714 1, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700715 cc_data_temp =
716 brcmf_sdio_regrl(sdiodev,
717 CORE_CC_REG(base, chipcontrol_addr),
718 NULL);
Franky Line12afb62011-11-04 22:23:40 +0100719 cc_data_temp &= ~str_mask;
720 drivestrength_sel <<= str_shift;
721 cc_data_temp |= drivestrength_sel;
Franky Line13ce262012-05-04 18:27:35 -0700722 brcmf_sdio_regwl(sdiodev, CORE_CC_REG(base, chipcontrol_addr),
723 cc_data_temp, NULL);
Franky Line12afb62011-11-04 22:23:40 +0100724
725 brcmf_dbg(INFO, "SDIO: %dmA drive strength selected, set to 0x%08x\n",
726 drivestrength, cc_data_temp);
727 }
728}
Franky Lin069eddd2013-04-11 13:28:48 +0200729
730#ifdef DEBUG
731static bool
732brcmf_sdio_chip_verifynvram(struct brcmf_sdio_dev *sdiodev, u32 nvram_addr,
733 char *nvram_dat, uint nvram_sz)
734{
735 char *nvram_ularray;
736 int err;
737 bool ret = true;
738
739 /* read back and verify */
740 brcmf_dbg(INFO, "Compare NVRAM dl & ul; size=%d\n", nvram_sz);
741 nvram_ularray = kmalloc(nvram_sz, GFP_KERNEL);
742 /* do not proceed while no memory but */
743 if (!nvram_ularray)
744 return true;
745
746 /* Upload image to verify downloaded contents. */
747 memset(nvram_ularray, 0xaa, nvram_sz);
748
749 /* Read the vars list to temp buffer for comparison */
750 err = brcmf_sdio_ramrw(sdiodev, false, nvram_addr, nvram_ularray,
751 nvram_sz);
752 if (err) {
753 brcmf_err("error %d on reading %d nvram bytes at 0x%08x\n",
754 err, nvram_sz, nvram_addr);
755 } else if (memcmp(nvram_dat, nvram_ularray, nvram_sz)) {
756 brcmf_err("Downloaded NVRAM image is corrupted\n");
757 ret = false;
758 }
759 kfree(nvram_ularray);
760
761 return ret;
762}
763#else /* DEBUG */
764static inline bool
765brcmf_sdio_chip_verifynvram(struct brcmf_sdio_dev *sdiodev, u32 nvram_addr,
766 char *nvram_dat, uint nvram_sz)
767{
768 return true;
769}
770#endif /* DEBUG */
771
772static bool brcmf_sdio_chip_writenvram(struct brcmf_sdio_dev *sdiodev,
773 struct chip_info *ci,
774 char *nvram_dat, uint nvram_sz)
775{
776 int err;
777 u32 nvram_addr;
778 u32 token;
779 __le32 token_le;
780
Franky Lin1640f282013-04-11 13:28:51 +0200781 nvram_addr = (ci->ramsize - 4) - nvram_sz + ci->rambase;
Franky Lin069eddd2013-04-11 13:28:48 +0200782
783 /* Write the vars list */
784 err = brcmf_sdio_ramrw(sdiodev, true, nvram_addr, nvram_dat, nvram_sz);
785 if (err) {
786 brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n",
787 err, nvram_sz, nvram_addr);
788 return false;
789 }
790
791 if (!brcmf_sdio_chip_verifynvram(sdiodev, nvram_addr,
792 nvram_dat, nvram_sz))
793 return false;
794
795 /* generate token:
796 * nvram size, converted to words, in lower 16-bits, checksum
797 * in upper 16-bits.
798 */
799 token = nvram_sz / 4;
800 token = (~token << 16) | (token & 0x0000FFFF);
801 token_le = cpu_to_le32(token);
802
803 brcmf_dbg(INFO, "RAM size: %d\n", ci->ramsize);
804 brcmf_dbg(INFO, "nvram is placed at %d, size %d, token=0x%08x\n",
805 nvram_addr, nvram_sz, token);
806
807 /* Write the length token to the last word */
Franky Lin1640f282013-04-11 13:28:51 +0200808 if (brcmf_sdio_ramrw(sdiodev, true, (ci->ramsize - 4 + ci->rambase),
Franky Lin069eddd2013-04-11 13:28:48 +0200809 (u8 *)&token_le, 4))
810 return false;
811
812 return true;
813}
814
815static void
816brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev,
817 struct chip_info *ci)
818{
819 u32 zeros = 0;
820
Franky Lin1640f282013-04-11 13:28:51 +0200821 ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0);
822 ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0);
Franky Lin069eddd2013-04-11 13:28:48 +0200823
824 /* clear length token */
825 brcmf_sdio_ramrw(sdiodev, true, ci->ramsize - 4, (u8 *)&zeros, 4);
826}
827
828static bool
829brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci,
830 char *nvram_dat, uint nvram_sz)
831{
832 u8 core_idx;
833 u32 reg_addr;
834
835 if (!ci->iscoreup(sdiodev, ci, BCMA_CORE_INTERNAL_MEM)) {
836 brcmf_err("SOCRAM core is down after reset?\n");
837 return false;
838 }
839
840 if (!brcmf_sdio_chip_writenvram(sdiodev, ci, nvram_dat, nvram_sz))
841 return false;
842
843 /* clear all interrupts */
844 core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV);
845 reg_addr = ci->c_inf[core_idx].base;
846 reg_addr += offsetof(struct sdpcmd_regs, intstatus);
847 brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
848
Franky Lin1640f282013-04-11 13:28:51 +0200849 ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3, 0);
850
851 return true;
852}
853
854static inline void
855brcmf_sdio_chip_cr4_enterdl(struct brcmf_sdio_dev *sdiodev,
856 struct chip_info *ci)
857{
858 ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4,
859 ARMCR4_BCMA_IOCTL_CPUHALT);
860}
861
862static bool
863brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci,
864 char *nvram_dat, uint nvram_sz)
865{
866 u8 core_idx;
867 u32 reg_addr;
868
869 if (!brcmf_sdio_chip_writenvram(sdiodev, ci, nvram_dat, nvram_sz))
870 return false;
871
872 /* clear all interrupts */
873 core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV);
874 reg_addr = ci->c_inf[core_idx].base;
875 reg_addr += offsetof(struct sdpcmd_regs, intstatus);
876 brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
877
878 /* Write reset vector to address 0 */
879 brcmf_sdio_ramrw(sdiodev, true, 0, (void *)&ci->rst_vec,
880 sizeof(ci->rst_vec));
881
882 /* restore ARM */
883 ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, 0);
Franky Lin069eddd2013-04-11 13:28:48 +0200884
885 return true;
886}
887
888void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev,
889 struct chip_info *ci)
890{
Franky Lin1640f282013-04-11 13:28:51 +0200891 u8 arm_core_idx;
892
893 arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
894 if (BRCMF_MAX_CORENUM != arm_core_idx) {
895 brcmf_sdio_chip_cm3_enterdl(sdiodev, ci);
896 return;
897 }
898
899 brcmf_sdio_chip_cr4_enterdl(sdiodev, ci);
Franky Lin069eddd2013-04-11 13:28:48 +0200900}
901
902bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev,
903 struct chip_info *ci, char *nvram_dat,
904 uint nvram_sz)
905{
Franky Lin1640f282013-04-11 13:28:51 +0200906 u8 arm_core_idx;
907
908 arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
909 if (BRCMF_MAX_CORENUM != arm_core_idx)
910 return brcmf_sdio_chip_cm3_exitdl(sdiodev, ci, nvram_dat,
911 nvram_sz);
912
913 return brcmf_sdio_chip_cr4_exitdl(sdiodev, ci, nvram_dat, nvram_sz);
Franky Lin069eddd2013-04-11 13:28:48 +0200914}