blob: 0ac048deb835b626d4e79f9c177be06cdccce2c8 [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
Hante Meuleman369508c2013-04-11 13:28:54 +020043/* 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
Franky Lina83369b2011-11-04 22:23:28 +010052#define SBCOREREV(sbidh) \
Franky Lin61213be2011-11-04 22:23:41 +010053 ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \
54 ((sbidh) & SSB_IDHIGH_RCLO))
Franky Lina83369b2011-11-04 22:23:28 +010055
Franky Lin6ca687d2011-11-10 20:30:21 +010056/* SOC Interconnect types (aka chip types) */
57#define SOCI_SB 0
58#define SOCI_AI 1
59
Franky Lin523894f2011-11-10 20:30:22 +010060/* EROM CompIdentB */
61#define CIB_REV_MASK 0xff000000
62#define CIB_REV_SHIFT 24
63
Franky Lin1640f282013-04-11 13:28:51 +020064/* ARM CR4 core specific control flag bits */
65#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020
66
Franky Line12afb62011-11-04 22:23:40 +010067#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu))
68/* SDIO Pad drive strength to select value mappings */
69struct sdiod_drive_str {
70 u8 strength; /* Pad Drive Strength in mA */
71 u8 sel; /* Chip-specific select value */
72};
Franky Lince2d7d72011-12-08 15:06:39 -080073/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */
Franky Linffb27562011-12-08 15:06:40 -080074static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = {
Franky Lince2d7d72011-12-08 15:06:39 -080075 {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
Franky Lin99ba15c2011-11-04 22:23:42 +010085u8
86brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid)
87{
88 u8 idx;
89
90 for (idx = 0; idx < BRCMF_MAX_CORENUM; idx++)
91 if (coreid == ci->c_inf[idx].id)
92 return idx;
93
94 return BRCMF_MAX_CORENUM;
95}
96
Franky Lin454d2a82011-11-04 22:23:37 +010097static u32
Franky Lin523894f2011-11-10 20:30:22 +010098brcmf_sdio_sb_corerev(struct brcmf_sdio_dev *sdiodev,
99 struct chip_info *ci, u16 coreid)
Franky Lin454d2a82011-11-04 22:23:37 +0100100{
101 u32 regdata;
Franky Lin523894f2011-11-10 20:30:22 +0100102 u8 idx;
103
104 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
Franky Lin454d2a82011-11-04 22:23:37 +0100105
Franky Lin79ae3952012-05-04 18:27:34 -0700106 regdata = brcmf_sdio_regrl(sdiodev,
107 CORE_SB(ci->c_inf[idx].base, sbidhigh),
108 NULL);
Franky Lin454d2a82011-11-04 22:23:37 +0100109 return SBCOREREV(regdata);
110}
111
Franky Lin523894f2011-11-10 20:30:22 +0100112static u32
113brcmf_sdio_ai_corerev(struct brcmf_sdio_dev *sdiodev,
114 struct chip_info *ci, u16 coreid)
115{
116 u8 idx;
117
118 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
119
120 return (ci->c_inf[idx].cib & CIB_REV_MASK) >> CIB_REV_SHIFT;
121}
122
Franky Lin6ca687d2011-11-10 20:30:21 +0100123static bool
124brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev,
125 struct chip_info *ci, u16 coreid)
Franky Lind8f64a42011-11-04 22:23:36 +0100126{
127 u32 regdata;
Franky Lin6ca687d2011-11-10 20:30:21 +0100128 u8 idx;
129
130 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
Franky Lind8f64a42011-11-04 22:23:36 +0100131
Franky Lin79ae3952012-05-04 18:27:34 -0700132 regdata = brcmf_sdio_regrl(sdiodev,
133 CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
134 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100135 regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT |
136 SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK);
137 return (SSB_TMSLOW_CLOCK == regdata);
Franky Lind8f64a42011-11-04 22:23:36 +0100138}
139
Franky Lin6ca687d2011-11-10 20:30:21 +0100140static bool
141brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev,
142 struct chip_info *ci, u16 coreid)
143{
144 u32 regdata;
145 u8 idx;
146 bool ret;
147
148 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
149
Franky Lin79ae3952012-05-04 18:27:34 -0700150 regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
151 NULL);
Franky Lin6ca687d2011-11-10 20:30:21 +0100152 ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK;
153
Franky Lin79ae3952012-05-04 18:27:34 -0700154 regdata = brcmf_sdio_regrl(sdiodev,
155 ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
156 NULL);
Franky Lin6ca687d2011-11-10 20:30:21 +0100157 ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0);
158
159 return ret;
160}
161
Franky Lin086a2e02011-11-10 20:30:23 +0100162static void
163brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev,
Franky Lin1640f282013-04-11 13:28:51 +0200164 struct chip_info *ci, u16 coreid, u32 core_bits)
Franky Lin2d4a9af2011-11-04 22:23:31 +0100165{
Franky Lin79ae3952012-05-04 18:27:34 -0700166 u32 regdata, base;
Franky Lin086a2e02011-11-10 20:30:23 +0100167 u8 idx;
168
169 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
Franky Lin79ae3952012-05-04 18:27:34 -0700170 base = ci->c_inf[idx].base;
Franky Lin2d4a9af2011-11-04 22:23:31 +0100171
Franky Lin79ae3952012-05-04 18:27:34 -0700172 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100173 if (regdata & SSB_TMSLOW_RESET)
Franky Lin2d4a9af2011-11-04 22:23:31 +0100174 return;
175
Franky Lin79ae3952012-05-04 18:27:34 -0700176 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100177 if ((regdata & SSB_TMSLOW_CLOCK) != 0) {
Franky Lin2d4a9af2011-11-04 22:23:31 +0100178 /*
179 * set target reject and spin until busy is clear
180 * (preserve core-specific bits)
181 */
Franky Lin79ae3952012-05-04 18:27:34 -0700182 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow),
183 NULL);
Franky Line13ce262012-05-04 18:27:35 -0700184 brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
185 regdata | SSB_TMSLOW_REJECT, NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100186
Franky Lin79ae3952012-05-04 18:27:34 -0700187 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow),
188 NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100189 udelay(1);
Franky Lin79ae3952012-05-04 18:27:34 -0700190 SPINWAIT((brcmf_sdio_regrl(sdiodev,
191 CORE_SB(base, sbtmstatehigh),
192 NULL) &
Franky Lin61213be2011-11-04 22:23:41 +0100193 SSB_TMSHIGH_BUSY), 100000);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100194
Franky Lin79ae3952012-05-04 18:27:34 -0700195 regdata = brcmf_sdio_regrl(sdiodev,
196 CORE_SB(base, sbtmstatehigh),
197 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100198 if (regdata & SSB_TMSHIGH_BUSY)
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100199 brcmf_err("core state still busy\n");
Franky Lin2d4a9af2011-11-04 22:23:31 +0100200
Franky Lin79ae3952012-05-04 18:27:34 -0700201 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow),
202 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100203 if (regdata & SSB_IDLOW_INITIATOR) {
Franky Lin79ae3952012-05-04 18:27:34 -0700204 regdata = brcmf_sdio_regrl(sdiodev,
205 CORE_SB(base, sbimstate),
206 NULL);
207 regdata |= SSB_IMSTATE_REJECT;
Franky Line13ce262012-05-04 18:27:35 -0700208 brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate),
209 regdata, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700210 regdata = brcmf_sdio_regrl(sdiodev,
211 CORE_SB(base, sbimstate),
212 NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100213 udelay(1);
Franky Lin79ae3952012-05-04 18:27:34 -0700214 SPINWAIT((brcmf_sdio_regrl(sdiodev,
215 CORE_SB(base, sbimstate),
216 NULL) &
Franky Lin61213be2011-11-04 22:23:41 +0100217 SSB_IMSTATE_BUSY), 100000);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100218 }
219
220 /* set reset and reject while enabling the clocks */
Franky Line13ce262012-05-04 18:27:35 -0700221 regdata = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
222 SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET;
223 brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
224 regdata, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700225 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow),
226 NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100227 udelay(10);
228
229 /* clear the initiator reject bit */
Franky Lin79ae3952012-05-04 18:27:34 -0700230 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow),
231 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100232 if (regdata & SSB_IDLOW_INITIATOR) {
Franky Lin79ae3952012-05-04 18:27:34 -0700233 regdata = brcmf_sdio_regrl(sdiodev,
234 CORE_SB(base, sbimstate),
235 NULL);
236 regdata &= ~SSB_IMSTATE_REJECT;
Franky Line13ce262012-05-04 18:27:35 -0700237 brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate),
238 regdata, NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100239 }
240 }
241
242 /* leave reset and reject asserted */
Franky Line13ce262012-05-04 18:27:35 -0700243 brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
244 (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET), NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100245 udelay(1);
246}
247
Franky Lin086a2e02011-11-10 20:30:23 +0100248static void
249brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev,
Franky Lin1640f282013-04-11 13:28:51 +0200250 struct chip_info *ci, u16 coreid, u32 core_bits)
Franky Lin086a2e02011-11-10 20:30:23 +0100251{
252 u8 idx;
253 u32 regdata;
254
255 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
256
257 /* if core is already in reset, just return */
Franky Lin79ae3952012-05-04 18:27:34 -0700258 regdata = brcmf_sdio_regrl(sdiodev,
259 ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
260 NULL);
Franky Lin086a2e02011-11-10 20:30:23 +0100261 if ((regdata & BCMA_RESET_CTL_RESET) != 0)
262 return;
263
Franky Lin1640f282013-04-11 13:28:51 +0200264 /* ensure no pending backplane operation
265 * 300uc should be sufficient for backplane ops to be finish
266 * extra 10ms is taken into account for firmware load stage
267 * after 10300us carry on disabling the core anyway
268 */
269 SPINWAIT(brcmf_sdio_regrl(sdiodev,
270 ci->c_inf[idx].wrapbase+BCMA_RESET_ST,
271 NULL), 10300);
272 regdata = brcmf_sdio_regrl(sdiodev,
273 ci->c_inf[idx].wrapbase+BCMA_RESET_ST,
Franky Lin79ae3952012-05-04 18:27:34 -0700274 NULL);
Franky Lin1640f282013-04-11 13:28:51 +0200275 if (regdata)
276 brcmf_err("disabling core 0x%x with reset status %x\n",
277 coreid, regdata);
Franky Lin086a2e02011-11-10 20:30:23 +0100278
Franky Line13ce262012-05-04 18:27:35 -0700279 brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
280 BCMA_RESET_CTL_RESET, NULL);
Franky Lin086a2e02011-11-10 20:30:23 +0100281 udelay(1);
Franky Lin1640f282013-04-11 13:28:51 +0200282
283 brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
284 core_bits, NULL);
285 regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
286 NULL);
287 usleep_range(10, 20);
288
Franky Lin086a2e02011-11-10 20:30:23 +0100289}
290
Franky Lind77e70f2011-11-10 20:30:24 +0100291static void
292brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev,
Franky Lin1640f282013-04-11 13:28:51 +0200293 struct chip_info *ci, u16 coreid, u32 core_bits)
Franky Lin2bc78e12011-11-04 22:23:38 +0100294{
295 u32 regdata;
Franky Lin086a2e02011-11-10 20:30:23 +0100296 u8 idx;
297
298 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
Franky Lin2bc78e12011-11-04 22:23:38 +0100299
300 /*
301 * Must do the disable sequence first to work for
302 * arbitrary current core state.
303 */
Franky Lin1640f282013-04-11 13:28:51 +0200304 brcmf_sdio_sb_coredisable(sdiodev, ci, coreid, 0);
Franky Lin2bc78e12011-11-04 22:23:38 +0100305
306 /*
307 * Now do the initialization sequence.
308 * set reset while enabling the clock and
309 * forcing them on throughout the core
310 */
Franky Line13ce262012-05-04 18:27:35 -0700311 brcmf_sdio_regwl(sdiodev,
312 CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
313 SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET,
314 NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700315 regdata = brcmf_sdio_regrl(sdiodev,
316 CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
317 NULL);
Franky Lin2bc78e12011-11-04 22:23:38 +0100318 udelay(1);
319
Franky Lind77e70f2011-11-10 20:30:24 +0100320 /* clear any serror */
Franky Lin79ae3952012-05-04 18:27:34 -0700321 regdata = brcmf_sdio_regrl(sdiodev,
322 CORE_SB(ci->c_inf[idx].base, sbtmstatehigh),
323 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100324 if (regdata & SSB_TMSHIGH_SERR)
Franky Line13ce262012-05-04 18:27:35 -0700325 brcmf_sdio_regwl(sdiodev,
326 CORE_SB(ci->c_inf[idx].base, sbtmstatehigh),
327 0, NULL);
Franky Lin2bc78e12011-11-04 22:23:38 +0100328
Franky Lin79ae3952012-05-04 18:27:34 -0700329 regdata = brcmf_sdio_regrl(sdiodev,
330 CORE_SB(ci->c_inf[idx].base, sbimstate),
331 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100332 if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO))
Franky Line13ce262012-05-04 18:27:35 -0700333 brcmf_sdio_regwl(sdiodev,
334 CORE_SB(ci->c_inf[idx].base, sbimstate),
335 regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO),
336 NULL);
Franky Lin2bc78e12011-11-04 22:23:38 +0100337
338 /* clear reset and allow it to propagate throughout the core */
Franky Line13ce262012-05-04 18:27:35 -0700339 brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
340 SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700341 regdata = brcmf_sdio_regrl(sdiodev,
342 CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
343 NULL);
Franky Lin2bc78e12011-11-04 22:23:38 +0100344 udelay(1);
345
346 /* leave clock enabled */
Franky Line13ce262012-05-04 18:27:35 -0700347 brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
348 SSB_TMSLOW_CLOCK, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700349 regdata = brcmf_sdio_regrl(sdiodev,
350 CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
351 NULL);
Franky Lind77e70f2011-11-10 20:30:24 +0100352 udelay(1);
353}
354
355static void
356brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev,
Franky Lin1640f282013-04-11 13:28:51 +0200357 struct chip_info *ci, u16 coreid, u32 core_bits)
Franky Lind77e70f2011-11-10 20:30:24 +0100358{
359 u8 idx;
360 u32 regdata;
361
362 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
363
364 /* must disable first to work for arbitrary current core state */
Franky Lin1640f282013-04-11 13:28:51 +0200365 brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, core_bits);
Franky Lind77e70f2011-11-10 20:30:24 +0100366
367 /* now do initialization sequence */
Franky Line13ce262012-05-04 18:27:35 -0700368 brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
Franky Lin1640f282013-04-11 13:28:51 +0200369 core_bits | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700370 regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
371 NULL);
Franky Line13ce262012-05-04 18:27:35 -0700372 brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
373 0, NULL);
Franky Lin1640f282013-04-11 13:28:51 +0200374 regdata = brcmf_sdio_regrl(sdiodev,
375 ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
376 NULL);
Franky Lind77e70f2011-11-10 20:30:24 +0100377 udelay(1);
378
Franky Line13ce262012-05-04 18:27:35 -0700379 brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
Franky Lin1640f282013-04-11 13:28:51 +0200380 core_bits | BCMA_IOCTL_CLK, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700381 regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
382 NULL);
Franky Lin2bc78e12011-11-04 22:23:38 +0100383 udelay(1);
384}
385
Franky Lin1640f282013-04-11 13:28:51 +0200386#ifdef DEBUG
387/* safety check for chipinfo */
388static int brcmf_sdio_chip_cichk(struct chip_info *ci)
389{
390 u8 core_idx;
391
392 /* check RAM core presence for ARM CM3 core */
393 core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
394 if (BRCMF_MAX_CORENUM != core_idx) {
395 core_idx = brcmf_sdio_chip_getinfidx(ci,
396 BCMA_CORE_INTERNAL_MEM);
397 if (BRCMF_MAX_CORENUM == core_idx) {
398 brcmf_err("RAM core not provided with ARM CM3 core\n");
399 return -ENODEV;
400 }
401 }
402
403 /* check RAM base for ARM CR4 core */
404 core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CR4);
405 if (BRCMF_MAX_CORENUM != core_idx) {
406 if (ci->rambase == 0) {
407 brcmf_err("RAM base not provided with ARM CR4 core\n");
408 return -ENOMEM;
409 }
410 }
411
412 return 0;
413}
414#else /* DEBUG */
415static inline int brcmf_sdio_chip_cichk(struct chip_info *ci)
416{
417 return 0;
418}
419#endif
420
Franky Lina83369b2011-11-04 22:23:28 +0100421static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev,
422 struct chip_info *ci, u32 regs)
423{
424 u32 regdata;
Franky Lin1640f282013-04-11 13:28:51 +0200425 int ret;
Franky Lina83369b2011-11-04 22:23:28 +0100426
Franky Lin069eddd2013-04-11 13:28:48 +0200427 /* Get CC core rev
Franky Lina83369b2011-11-04 22:23:28 +0100428 * Chipid is assume to be at offset 0 from regs arg
429 * For different chiptypes or old sdio hosts w/o chipcommon,
430 * other ways of recognition should be added here.
431 */
Franky Lin99ba15c2011-11-04 22:23:42 +0100432 ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON;
433 ci->c_inf[0].base = regs;
Franky Lin79ae3952012-05-04 18:27:34 -0700434 regdata = brcmf_sdio_regrl(sdiodev,
435 CORE_CC_REG(ci->c_inf[0].base, chipid),
436 NULL);
Franky Lina83369b2011-11-04 22:23:28 +0100437 ci->chip = regdata & CID_ID_MASK;
438 ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
Franky Lin6ca687d2011-11-10 20:30:21 +0100439 ci->socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT;
Franky Lina83369b2011-11-04 22:23:28 +0100440
441 brcmf_dbg(INFO, "chipid=0x%x chiprev=%d\n", ci->chip, ci->chiprev);
442
443 /* Address of cores for new chips should be added here */
444 switch (ci->chip) {
Hante Meuleman369508c2013-04-11 13:28:54 +0200445 case BCM43143_CHIP_ID:
446 ci->c_inf[0].wrapbase = ci->c_inf[0].base + 0x00100000;
447 ci->c_inf[0].cib = 0x2b000000;
448 ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
449 ci->c_inf[1].base = BCM43143_CORE_BUS_BASE;
450 ci->c_inf[1].wrapbase = ci->c_inf[1].base + 0x00100000;
451 ci->c_inf[1].cib = 0x18000000;
452 ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
453 ci->c_inf[2].base = BCM43143_CORE_SOCRAM_BASE;
454 ci->c_inf[2].wrapbase = ci->c_inf[2].base + 0x00100000;
455 ci->c_inf[2].cib = 0x14000000;
456 ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
457 ci->c_inf[3].base = BCM43143_CORE_ARM_BASE;
458 ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000;
459 ci->c_inf[3].cib = 0x07000000;
460 ci->ramsize = BCM43143_RAMSIZE;
461 break;
Franky Lin4a1c02c2012-08-30 19:42:59 +0200462 case BCM43241_CHIP_ID:
463 ci->c_inf[0].wrapbase = 0x18100000;
464 ci->c_inf[0].cib = 0x2a084411;
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 = 0x0e004211;
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 = 0x14080401;
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 = 0x07004211;
477 ci->ramsize = 0x90000;
478 break;
Franky Lina83369b2011-11-04 22:23:28 +0100479 case BCM4329_CHIP_ID:
Franky Lin99ba15c2011-11-04 22:23:42 +0100480 ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
481 ci->c_inf[1].base = BCM4329_CORE_BUS_BASE;
482 ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
483 ci->c_inf[2].base = BCM4329_CORE_SOCRAM_BASE;
484 ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
485 ci->c_inf[3].base = BCM4329_CORE_ARM_BASE;
Franky Lina83369b2011-11-04 22:23:28 +0100486 ci->ramsize = BCM4329_RAMSIZE;
487 break;
Franky Lince2d7d72011-12-08 15:06:39 -0800488 case BCM4330_CHIP_ID:
489 ci->c_inf[0].wrapbase = 0x18100000;
490 ci->c_inf[0].cib = 0x27004211;
491 ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
492 ci->c_inf[1].base = 0x18002000;
493 ci->c_inf[1].wrapbase = 0x18102000;
494 ci->c_inf[1].cib = 0x07004211;
495 ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
496 ci->c_inf[2].base = 0x18004000;
497 ci->c_inf[2].wrapbase = 0x18104000;
498 ci->c_inf[2].cib = 0x0d080401;
499 ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
500 ci->c_inf[3].base = 0x18003000;
501 ci->c_inf[3].wrapbase = 0x18103000;
502 ci->c_inf[3].cib = 0x03004211;
503 ci->ramsize = 0x48000;
504 break;
Franky Lin85a4a1c2012-06-26 21:26:39 +0200505 case BCM4334_CHIP_ID:
506 ci->c_inf[0].wrapbase = 0x18100000;
507 ci->c_inf[0].cib = 0x29004211;
508 ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
509 ci->c_inf[1].base = 0x18002000;
510 ci->c_inf[1].wrapbase = 0x18102000;
511 ci->c_inf[1].cib = 0x0d004211;
512 ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
513 ci->c_inf[2].base = 0x18004000;
514 ci->c_inf[2].wrapbase = 0x18104000;
515 ci->c_inf[2].cib = 0x13080401;
516 ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
517 ci->c_inf[3].base = 0x18003000;
518 ci->c_inf[3].wrapbase = 0x18103000;
519 ci->c_inf[3].cib = 0x07004211;
520 ci->ramsize = 0x80000;
521 break;
Franky Lin6a1c7482013-04-11 13:28:53 +0200522 case BCM4335_CHIP_ID:
523 ci->c_inf[0].wrapbase = 0x18100000;
524 ci->c_inf[0].cib = 0x2b084411;
525 ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
526 ci->c_inf[1].base = 0x18005000;
527 ci->c_inf[1].wrapbase = 0x18105000;
528 ci->c_inf[1].cib = 0x0f004211;
529 ci->c_inf[2].id = BCMA_CORE_ARM_CR4;
530 ci->c_inf[2].base = 0x18002000;
531 ci->c_inf[2].wrapbase = 0x18102000;
532 ci->c_inf[2].cib = 0x01084411;
533 ci->ramsize = 0xc0000;
534 ci->rambase = 0x180000;
535 break;
Franky Lina83369b2011-11-04 22:23:28 +0100536 default:
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100537 brcmf_err("chipid 0x%x is not supported\n", ci->chip);
Franky Lina83369b2011-11-04 22:23:28 +0100538 return -ENODEV;
539 }
540
Franky Lin1640f282013-04-11 13:28:51 +0200541 ret = brcmf_sdio_chip_cichk(ci);
542 if (ret)
543 return ret;
544
Franky Lin6ca687d2011-11-10 20:30:21 +0100545 switch (ci->socitype) {
546 case SOCI_SB:
547 ci->iscoreup = brcmf_sdio_sb_iscoreup;
Franky Lin523894f2011-11-10 20:30:22 +0100548 ci->corerev = brcmf_sdio_sb_corerev;
Franky Lin086a2e02011-11-10 20:30:23 +0100549 ci->coredisable = brcmf_sdio_sb_coredisable;
Franky Lind77e70f2011-11-10 20:30:24 +0100550 ci->resetcore = brcmf_sdio_sb_resetcore;
Franky Lin6ca687d2011-11-10 20:30:21 +0100551 break;
552 case SOCI_AI:
553 ci->iscoreup = brcmf_sdio_ai_iscoreup;
Franky Lin523894f2011-11-10 20:30:22 +0100554 ci->corerev = brcmf_sdio_ai_corerev;
Franky Lin086a2e02011-11-10 20:30:23 +0100555 ci->coredisable = brcmf_sdio_ai_coredisable;
Franky Lind77e70f2011-11-10 20:30:24 +0100556 ci->resetcore = brcmf_sdio_ai_resetcore;
Franky Lin6ca687d2011-11-10 20:30:21 +0100557 break;
558 default:
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100559 brcmf_err("socitype %u not supported\n", ci->socitype);
Franky Lin6ca687d2011-11-10 20:30:21 +0100560 return -ENODEV;
561 }
562
Franky Lina83369b2011-11-04 22:23:28 +0100563 return 0;
564}
565
Franky Line63ac6b2011-11-04 22:23:29 +0100566static int
567brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev)
568{
569 int err = 0;
570 u8 clkval, clkset;
571
572 /* Try forcing SDIO core to do ALPAvail request only */
573 clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
Franky Lin3bba8292012-05-04 18:27:33 -0700574 brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
Franky Line63ac6b2011-11-04 22:23:29 +0100575 if (err) {
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100576 brcmf_err("error writing for HT off\n");
Franky Line63ac6b2011-11-04 22:23:29 +0100577 return err;
578 }
579
580 /* If register supported, wait for ALPAvail and then force ALP */
581 /* This may take up to 15 milliseconds */
Franky Lin45db3392012-05-04 18:27:32 -0700582 clkval = brcmf_sdio_regrb(sdiodev,
583 SBSDIO_FUNC1_CHIPCLKCSR, NULL);
Franky Line63ac6b2011-11-04 22:23:29 +0100584
585 if ((clkval & ~SBSDIO_AVBITS) != clkset) {
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100586 brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
Franky Line63ac6b2011-11-04 22:23:29 +0100587 clkset, clkval);
588 return -EACCES;
589 }
590
Franky Lin45db3392012-05-04 18:27:32 -0700591 SPINWAIT(((clkval = brcmf_sdio_regrb(sdiodev,
592 SBSDIO_FUNC1_CHIPCLKCSR, NULL)),
Franky Line63ac6b2011-11-04 22:23:29 +0100593 !SBSDIO_ALPAV(clkval)),
594 PMU_MAX_TRANSITION_DLY);
595 if (!SBSDIO_ALPAV(clkval)) {
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100596 brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n",
Franky Line63ac6b2011-11-04 22:23:29 +0100597 clkval);
598 return -EBUSY;
599 }
600
601 clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP;
Franky Lin3bba8292012-05-04 18:27:33 -0700602 brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
Franky Line63ac6b2011-11-04 22:23:29 +0100603 udelay(65);
604
605 /* Also, disable the extra SDIO pull-ups */
Franky Lin3bba8292012-05-04 18:27:33 -0700606 brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
Franky Line63ac6b2011-11-04 22:23:29 +0100607
608 return 0;
609}
610
Franky Lin5b45e542011-11-04 22:23:30 +0100611static void
612brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev,
613 struct chip_info *ci)
614{
Franky Lin79ae3952012-05-04 18:27:34 -0700615 u32 base = ci->c_inf[0].base;
616
Franky Lin5b45e542011-11-04 22:23:30 +0100617 /* get chipcommon rev */
Franky Lin523894f2011-11-10 20:30:22 +0100618 ci->c_inf[0].rev = ci->corerev(sdiodev, ci, ci->c_inf[0].id);
Franky Lin5b45e542011-11-04 22:23:30 +0100619
620 /* get chipcommon capabilites */
Franky Lin79ae3952012-05-04 18:27:34 -0700621 ci->c_inf[0].caps = brcmf_sdio_regrl(sdiodev,
622 CORE_CC_REG(base, capabilities),
623 NULL);
Franky Lin5b45e542011-11-04 22:23:30 +0100624
625 /* get pmu caps & rev */
Franky Lin99ba15c2011-11-04 22:23:42 +0100626 if (ci->c_inf[0].caps & CC_CAP_PMU) {
Franky Lin79ae3952012-05-04 18:27:34 -0700627 ci->pmucaps =
628 brcmf_sdio_regrl(sdiodev,
629 CORE_CC_REG(base, pmucapabilities),
630 NULL);
Franky Lin5b45e542011-11-04 22:23:30 +0100631 ci->pmurev = ci->pmucaps & PCAP_REV_MASK;
632 }
633
Franky Lin523894f2011-11-10 20:30:22 +0100634 ci->c_inf[1].rev = ci->corerev(sdiodev, ci, ci->c_inf[1].id);
Franky Lin5b45e542011-11-04 22:23:30 +0100635
636 brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n",
Franky Lin99ba15c2011-11-04 22:23:42 +0100637 ci->c_inf[0].rev, ci->pmurev,
638 ci->c_inf[1].rev, ci->c_inf[1].id);
Franky Lin966414d2011-11-04 22:23:32 +0100639
640 /*
641 * Make sure any on-chip ARM is off (in case strapping is wrong),
642 * or downloaded code was already running.
643 */
Franky Lin1640f282013-04-11 13:28:51 +0200644 ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0);
Franky Lin5b45e542011-11-04 22:23:30 +0100645}
646
Franky Lina83369b2011-11-04 22:23:28 +0100647int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev,
Franky Lina97e4fc2011-11-04 22:23:35 +0100648 struct chip_info **ci_ptr, u32 regs)
Franky Lina83369b2011-11-04 22:23:28 +0100649{
Franky Lina97e4fc2011-11-04 22:23:35 +0100650 int ret;
651 struct chip_info *ci;
652
653 brcmf_dbg(TRACE, "Enter\n");
654
655 /* alloc chip_info_t */
656 ci = kzalloc(sizeof(struct chip_info), GFP_ATOMIC);
657 if (!ci)
658 return -ENOMEM;
Franky Lina83369b2011-11-04 22:23:28 +0100659
Franky Line63ac6b2011-11-04 22:23:29 +0100660 ret = brcmf_sdio_chip_buscoreprep(sdiodev);
661 if (ret != 0)
Franky Lina97e4fc2011-11-04 22:23:35 +0100662 goto err;
Franky Line63ac6b2011-11-04 22:23:29 +0100663
Franky Lina83369b2011-11-04 22:23:28 +0100664 ret = brcmf_sdio_chip_recognition(sdiodev, ci, regs);
665 if (ret != 0)
Franky Lina97e4fc2011-11-04 22:23:35 +0100666 goto err;
Franky Lina83369b2011-11-04 22:23:28 +0100667
Franky Lin5b45e542011-11-04 22:23:30 +0100668 brcmf_sdio_chip_buscoresetup(sdiodev, ci);
669
Franky Line13ce262012-05-04 18:27:35 -0700670 brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopullup),
671 0, NULL);
672 brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopulldown),
673 0, NULL);
Franky Lin960908d2011-11-04 22:23:33 +0100674
Franky Lina97e4fc2011-11-04 22:23:35 +0100675 *ci_ptr = ci;
676 return 0;
677
678err:
679 kfree(ci);
Franky Lina83369b2011-11-04 22:23:28 +0100680 return ret;
681}
Franky Lina8a6c042011-11-04 22:23:39 +0100682
683void
684brcmf_sdio_chip_detach(struct chip_info **ci_ptr)
685{
686 brcmf_dbg(TRACE, "Enter\n");
687
688 kfree(*ci_ptr);
689 *ci_ptr = NULL;
690}
Franky Line12afb62011-11-04 22:23:40 +0100691
692static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len)
693{
694 const char *fmt;
695
696 fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
697 snprintf(buf, len, fmt, chipid);
698 return buf;
699}
700
701void
702brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
703 struct chip_info *ci, u32 drivestrength)
704{
705 struct sdiod_drive_str *str_tab = NULL;
706 u32 str_mask = 0;
707 u32 str_shift = 0;
708 char chn[8];
Franky Lin79ae3952012-05-04 18:27:34 -0700709 u32 base = ci->c_inf[0].base;
Franky Line12afb62011-11-04 22:23:40 +0100710
Franky Lin99ba15c2011-11-04 22:23:42 +0100711 if (!(ci->c_inf[0].caps & CC_CAP_PMU))
Franky Line12afb62011-11-04 22:23:40 +0100712 return;
713
714 switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) {
Franky Lince2d7d72011-12-08 15:06:39 -0800715 case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12):
Franky Linffb27562011-12-08 15:06:40 -0800716 str_tab = (struct sdiod_drive_str *)&sdiod_drvstr_tab1_1v8;
Franky Lince2d7d72011-12-08 15:06:39 -0800717 str_mask = 0x00003800;
718 str_shift = 11;
719 break;
Franky Line12afb62011-11-04 22:23:40 +0100720 default:
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100721 brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n",
Franky Line12afb62011-11-04 22:23:40 +0100722 brcmf_sdio_chip_name(ci->chip, chn, 8),
723 ci->chiprev, ci->pmurev);
724 break;
725 }
726
727 if (str_tab != NULL) {
728 u32 drivestrength_sel = 0;
729 u32 cc_data_temp;
730 int i;
731
732 for (i = 0; str_tab[i].strength != 0; i++) {
733 if (drivestrength >= str_tab[i].strength) {
734 drivestrength_sel = str_tab[i].sel;
735 break;
736 }
737 }
738
Franky Line13ce262012-05-04 18:27:35 -0700739 brcmf_sdio_regwl(sdiodev, CORE_CC_REG(base, chipcontrol_addr),
740 1, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700741 cc_data_temp =
742 brcmf_sdio_regrl(sdiodev,
743 CORE_CC_REG(base, chipcontrol_addr),
744 NULL);
Franky Line12afb62011-11-04 22:23:40 +0100745 cc_data_temp &= ~str_mask;
746 drivestrength_sel <<= str_shift;
747 cc_data_temp |= drivestrength_sel;
Franky Line13ce262012-05-04 18:27:35 -0700748 brcmf_sdio_regwl(sdiodev, CORE_CC_REG(base, chipcontrol_addr),
749 cc_data_temp, NULL);
Franky Line12afb62011-11-04 22:23:40 +0100750
751 brcmf_dbg(INFO, "SDIO: %dmA drive strength selected, set to 0x%08x\n",
752 drivestrength, cc_data_temp);
753 }
754}
Franky Lin069eddd2013-04-11 13:28:48 +0200755
756#ifdef DEBUG
757static bool
758brcmf_sdio_chip_verifynvram(struct brcmf_sdio_dev *sdiodev, u32 nvram_addr,
759 char *nvram_dat, uint nvram_sz)
760{
761 char *nvram_ularray;
762 int err;
763 bool ret = true;
764
765 /* read back and verify */
766 brcmf_dbg(INFO, "Compare NVRAM dl & ul; size=%d\n", nvram_sz);
767 nvram_ularray = kmalloc(nvram_sz, GFP_KERNEL);
768 /* do not proceed while no memory but */
769 if (!nvram_ularray)
770 return true;
771
772 /* Upload image to verify downloaded contents. */
773 memset(nvram_ularray, 0xaa, nvram_sz);
774
775 /* Read the vars list to temp buffer for comparison */
776 err = brcmf_sdio_ramrw(sdiodev, false, nvram_addr, nvram_ularray,
777 nvram_sz);
778 if (err) {
779 brcmf_err("error %d on reading %d nvram bytes at 0x%08x\n",
780 err, nvram_sz, nvram_addr);
781 } else if (memcmp(nvram_dat, nvram_ularray, nvram_sz)) {
782 brcmf_err("Downloaded NVRAM image is corrupted\n");
783 ret = false;
784 }
785 kfree(nvram_ularray);
786
787 return ret;
788}
789#else /* DEBUG */
790static inline bool
791brcmf_sdio_chip_verifynvram(struct brcmf_sdio_dev *sdiodev, u32 nvram_addr,
792 char *nvram_dat, uint nvram_sz)
793{
794 return true;
795}
796#endif /* DEBUG */
797
798static bool brcmf_sdio_chip_writenvram(struct brcmf_sdio_dev *sdiodev,
799 struct chip_info *ci,
800 char *nvram_dat, uint nvram_sz)
801{
802 int err;
803 u32 nvram_addr;
804 u32 token;
805 __le32 token_le;
806
Franky Lin1640f282013-04-11 13:28:51 +0200807 nvram_addr = (ci->ramsize - 4) - nvram_sz + ci->rambase;
Franky Lin069eddd2013-04-11 13:28:48 +0200808
809 /* Write the vars list */
810 err = brcmf_sdio_ramrw(sdiodev, true, nvram_addr, nvram_dat, nvram_sz);
811 if (err) {
812 brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n",
813 err, nvram_sz, nvram_addr);
814 return false;
815 }
816
817 if (!brcmf_sdio_chip_verifynvram(sdiodev, nvram_addr,
818 nvram_dat, nvram_sz))
819 return false;
820
821 /* generate token:
822 * nvram size, converted to words, in lower 16-bits, checksum
823 * in upper 16-bits.
824 */
825 token = nvram_sz / 4;
826 token = (~token << 16) | (token & 0x0000FFFF);
827 token_le = cpu_to_le32(token);
828
829 brcmf_dbg(INFO, "RAM size: %d\n", ci->ramsize);
830 brcmf_dbg(INFO, "nvram is placed at %d, size %d, token=0x%08x\n",
831 nvram_addr, nvram_sz, token);
832
833 /* Write the length token to the last word */
Franky Lin1640f282013-04-11 13:28:51 +0200834 if (brcmf_sdio_ramrw(sdiodev, true, (ci->ramsize - 4 + ci->rambase),
Franky Lin069eddd2013-04-11 13:28:48 +0200835 (u8 *)&token_le, 4))
836 return false;
837
838 return true;
839}
840
841static void
842brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev,
843 struct chip_info *ci)
844{
845 u32 zeros = 0;
846
Franky Lin1640f282013-04-11 13:28:51 +0200847 ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0);
848 ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0);
Franky Lin069eddd2013-04-11 13:28:48 +0200849
850 /* clear length token */
851 brcmf_sdio_ramrw(sdiodev, true, ci->ramsize - 4, (u8 *)&zeros, 4);
852}
853
854static bool
855brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci,
856 char *nvram_dat, uint nvram_sz)
857{
858 u8 core_idx;
859 u32 reg_addr;
860
861 if (!ci->iscoreup(sdiodev, ci, BCMA_CORE_INTERNAL_MEM)) {
862 brcmf_err("SOCRAM core is down after reset?\n");
863 return false;
864 }
865
866 if (!brcmf_sdio_chip_writenvram(sdiodev, ci, nvram_dat, nvram_sz))
867 return false;
868
869 /* clear all interrupts */
870 core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV);
871 reg_addr = ci->c_inf[core_idx].base;
872 reg_addr += offsetof(struct sdpcmd_regs, intstatus);
873 brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
874
Franky Lin1640f282013-04-11 13:28:51 +0200875 ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3, 0);
876
877 return true;
878}
879
880static inline void
881brcmf_sdio_chip_cr4_enterdl(struct brcmf_sdio_dev *sdiodev,
882 struct chip_info *ci)
883{
884 ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4,
885 ARMCR4_BCMA_IOCTL_CPUHALT);
886}
887
888static bool
889brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci,
890 char *nvram_dat, uint nvram_sz)
891{
892 u8 core_idx;
893 u32 reg_addr;
894
895 if (!brcmf_sdio_chip_writenvram(sdiodev, ci, nvram_dat, nvram_sz))
896 return false;
897
898 /* clear all interrupts */
899 core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV);
900 reg_addr = ci->c_inf[core_idx].base;
901 reg_addr += offsetof(struct sdpcmd_regs, intstatus);
902 brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
903
904 /* Write reset vector to address 0 */
905 brcmf_sdio_ramrw(sdiodev, true, 0, (void *)&ci->rst_vec,
906 sizeof(ci->rst_vec));
907
908 /* restore ARM */
909 ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, 0);
Franky Lin069eddd2013-04-11 13:28:48 +0200910
911 return true;
912}
913
914void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev,
915 struct chip_info *ci)
916{
Franky Lin1640f282013-04-11 13:28:51 +0200917 u8 arm_core_idx;
918
919 arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
920 if (BRCMF_MAX_CORENUM != arm_core_idx) {
921 brcmf_sdio_chip_cm3_enterdl(sdiodev, ci);
922 return;
923 }
924
925 brcmf_sdio_chip_cr4_enterdl(sdiodev, ci);
Franky Lin069eddd2013-04-11 13:28:48 +0200926}
927
928bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev,
929 struct chip_info *ci, char *nvram_dat,
930 uint nvram_sz)
931{
Franky Lin1640f282013-04-11 13:28:51 +0200932 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 return brcmf_sdio_chip_cm3_exitdl(sdiodev, ci, nvram_dat,
937 nvram_sz);
938
939 return brcmf_sdio_chip_cr4_exitdl(sdiodev, ci, nvram_dat, nvram_sz);
Franky Lin069eddd2013-04-11 13:28:48 +0200940}