blob: 9818598f30ea856e1f7693201b506bf506f35e19 [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 Line12afb62011-11-04 22:23:40 +010055#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu))
56/* SDIO Pad drive strength to select value mappings */
57struct sdiod_drive_str {
58 u8 strength; /* Pad Drive Strength in mA */
59 u8 sel; /* Chip-specific select value */
60};
Franky Lince2d7d72011-12-08 15:06:39 -080061/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */
Franky Linffb27562011-12-08 15:06:40 -080062static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = {
Franky Lince2d7d72011-12-08 15:06:39 -080063 {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
Franky Lin99ba15c2011-11-04 22:23:42 +010073u8
74brcmf_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
Franky Lin454d2a82011-11-04 22:23:37 +010085static u32
Franky Lin523894f2011-11-10 20:30:22 +010086brcmf_sdio_sb_corerev(struct brcmf_sdio_dev *sdiodev,
87 struct chip_info *ci, u16 coreid)
Franky Lin454d2a82011-11-04 22:23:37 +010088{
89 u32 regdata;
Franky Lin523894f2011-11-10 20:30:22 +010090 u8 idx;
91
92 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
Franky Lin454d2a82011-11-04 22:23:37 +010093
Franky Lin79ae3952012-05-04 18:27:34 -070094 regdata = brcmf_sdio_regrl(sdiodev,
95 CORE_SB(ci->c_inf[idx].base, sbidhigh),
96 NULL);
Franky Lin454d2a82011-11-04 22:23:37 +010097 return SBCOREREV(regdata);
98}
99
Franky Lin523894f2011-11-10 20:30:22 +0100100static u32
101brcmf_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
Franky Lin6ca687d2011-11-10 20:30:21 +0100111static bool
112brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev,
113 struct chip_info *ci, u16 coreid)
Franky Lind8f64a42011-11-04 22:23:36 +0100114{
115 u32 regdata;
Franky Lin6ca687d2011-11-10 20:30:21 +0100116 u8 idx;
117
118 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
Franky Lind8f64a42011-11-04 22:23:36 +0100119
Franky Lin79ae3952012-05-04 18:27:34 -0700120 regdata = brcmf_sdio_regrl(sdiodev,
121 CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
122 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100123 regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT |
124 SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK);
125 return (SSB_TMSLOW_CLOCK == regdata);
Franky Lind8f64a42011-11-04 22:23:36 +0100126}
127
Franky Lin6ca687d2011-11-10 20:30:21 +0100128static bool
129brcmf_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
Franky Lin79ae3952012-05-04 18:27:34 -0700138 regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
139 NULL);
Franky Lin6ca687d2011-11-10 20:30:21 +0100140 ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK;
141
Franky Lin79ae3952012-05-04 18:27:34 -0700142 regdata = brcmf_sdio_regrl(sdiodev,
143 ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
144 NULL);
Franky Lin6ca687d2011-11-10 20:30:21 +0100145 ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0);
146
147 return ret;
148}
149
Franky Lin086a2e02011-11-10 20:30:23 +0100150static void
151brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev,
152 struct chip_info *ci, u16 coreid)
Franky Lin2d4a9af2011-11-04 22:23:31 +0100153{
Franky Lin79ae3952012-05-04 18:27:34 -0700154 u32 regdata, base;
Franky Lin086a2e02011-11-10 20:30:23 +0100155 u8 idx;
156
157 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
Franky Lin79ae3952012-05-04 18:27:34 -0700158 base = ci->c_inf[idx].base;
Franky Lin2d4a9af2011-11-04 22:23:31 +0100159
Franky Lin79ae3952012-05-04 18:27:34 -0700160 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100161 if (regdata & SSB_TMSLOW_RESET)
Franky Lin2d4a9af2011-11-04 22:23:31 +0100162 return;
163
Franky Lin79ae3952012-05-04 18:27:34 -0700164 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100165 if ((regdata & SSB_TMSLOW_CLOCK) != 0) {
Franky Lin2d4a9af2011-11-04 22:23:31 +0100166 /*
167 * set target reject and spin until busy is clear
168 * (preserve core-specific bits)
169 */
Franky Lin79ae3952012-05-04 18:27:34 -0700170 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow),
171 NULL);
Franky Line13ce262012-05-04 18:27:35 -0700172 brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
173 regdata | SSB_TMSLOW_REJECT, NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100174
Franky Lin79ae3952012-05-04 18:27:34 -0700175 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow),
176 NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100177 udelay(1);
Franky Lin79ae3952012-05-04 18:27:34 -0700178 SPINWAIT((brcmf_sdio_regrl(sdiodev,
179 CORE_SB(base, sbtmstatehigh),
180 NULL) &
Franky Lin61213be2011-11-04 22:23:41 +0100181 SSB_TMSHIGH_BUSY), 100000);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100182
Franky Lin79ae3952012-05-04 18:27:34 -0700183 regdata = brcmf_sdio_regrl(sdiodev,
184 CORE_SB(base, sbtmstatehigh),
185 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100186 if (regdata & SSB_TMSHIGH_BUSY)
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100187 brcmf_err("core state still busy\n");
Franky Lin2d4a9af2011-11-04 22:23:31 +0100188
Franky Lin79ae3952012-05-04 18:27:34 -0700189 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow),
190 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100191 if (regdata & SSB_IDLOW_INITIATOR) {
Franky Lin79ae3952012-05-04 18:27:34 -0700192 regdata = brcmf_sdio_regrl(sdiodev,
193 CORE_SB(base, sbimstate),
194 NULL);
195 regdata |= SSB_IMSTATE_REJECT;
Franky Line13ce262012-05-04 18:27:35 -0700196 brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate),
197 regdata, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700198 regdata = brcmf_sdio_regrl(sdiodev,
199 CORE_SB(base, sbimstate),
200 NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100201 udelay(1);
Franky Lin79ae3952012-05-04 18:27:34 -0700202 SPINWAIT((brcmf_sdio_regrl(sdiodev,
203 CORE_SB(base, sbimstate),
204 NULL) &
Franky Lin61213be2011-11-04 22:23:41 +0100205 SSB_IMSTATE_BUSY), 100000);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100206 }
207
208 /* set reset and reject while enabling the clocks */
Franky Line13ce262012-05-04 18:27:35 -0700209 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);
Franky Lin79ae3952012-05-04 18:27:34 -0700213 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow),
214 NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100215 udelay(10);
216
217 /* clear the initiator reject bit */
Franky Lin79ae3952012-05-04 18:27:34 -0700218 regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow),
219 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100220 if (regdata & SSB_IDLOW_INITIATOR) {
Franky Lin79ae3952012-05-04 18:27:34 -0700221 regdata = brcmf_sdio_regrl(sdiodev,
222 CORE_SB(base, sbimstate),
223 NULL);
224 regdata &= ~SSB_IMSTATE_REJECT;
Franky Line13ce262012-05-04 18:27:35 -0700225 brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate),
226 regdata, NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100227 }
228 }
229
230 /* leave reset and reject asserted */
Franky Line13ce262012-05-04 18:27:35 -0700231 brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
232 (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET), NULL);
Franky Lin2d4a9af2011-11-04 22:23:31 +0100233 udelay(1);
234}
235
Franky Lin086a2e02011-11-10 20:30:23 +0100236static void
237brcmf_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 */
Franky Lin79ae3952012-05-04 18:27:34 -0700246 regdata = brcmf_sdio_regrl(sdiodev,
247 ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
248 NULL);
Franky Lin086a2e02011-11-10 20:30:23 +0100249 if ((regdata & BCMA_RESET_CTL_RESET) != 0)
250 return;
251
Franky Line13ce262012-05-04 18:27:35 -0700252 brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, 0, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700253 regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
254 NULL);
Franky Lin086a2e02011-11-10 20:30:23 +0100255 udelay(10);
256
Franky Line13ce262012-05-04 18:27:35 -0700257 brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
258 BCMA_RESET_CTL_RESET, NULL);
Franky Lin086a2e02011-11-10 20:30:23 +0100259 udelay(1);
260}
261
Franky Lind77e70f2011-11-10 20:30:24 +0100262static void
263brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev,
264 struct chip_info *ci, u16 coreid)
Franky Lin2bc78e12011-11-04 22:23:38 +0100265{
266 u32 regdata;
Franky Lin086a2e02011-11-10 20:30:23 +0100267 u8 idx;
268
269 idx = brcmf_sdio_chip_getinfidx(ci, coreid);
Franky Lin2bc78e12011-11-04 22:23:38 +0100270
271 /*
272 * Must do the disable sequence first to work for
273 * arbitrary current core state.
274 */
Franky Lind77e70f2011-11-10 20:30:24 +0100275 brcmf_sdio_sb_coredisable(sdiodev, ci, coreid);
Franky Lin2bc78e12011-11-04 22:23:38 +0100276
277 /*
278 * Now do the initialization sequence.
279 * set reset while enabling the clock and
280 * forcing them on throughout the core
281 */
Franky Line13ce262012-05-04 18:27:35 -0700282 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);
Franky Lin79ae3952012-05-04 18:27:34 -0700286 regdata = brcmf_sdio_regrl(sdiodev,
287 CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
288 NULL);
Franky Lin2bc78e12011-11-04 22:23:38 +0100289 udelay(1);
290
Franky Lind77e70f2011-11-10 20:30:24 +0100291 /* clear any serror */
Franky Lin79ae3952012-05-04 18:27:34 -0700292 regdata = brcmf_sdio_regrl(sdiodev,
293 CORE_SB(ci->c_inf[idx].base, sbtmstatehigh),
294 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100295 if (regdata & SSB_TMSHIGH_SERR)
Franky Line13ce262012-05-04 18:27:35 -0700296 brcmf_sdio_regwl(sdiodev,
297 CORE_SB(ci->c_inf[idx].base, sbtmstatehigh),
298 0, NULL);
Franky Lin2bc78e12011-11-04 22:23:38 +0100299
Franky Lin79ae3952012-05-04 18:27:34 -0700300 regdata = brcmf_sdio_regrl(sdiodev,
301 CORE_SB(ci->c_inf[idx].base, sbimstate),
302 NULL);
Franky Lin61213be2011-11-04 22:23:41 +0100303 if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO))
Franky Line13ce262012-05-04 18:27:35 -0700304 brcmf_sdio_regwl(sdiodev,
305 CORE_SB(ci->c_inf[idx].base, sbimstate),
306 regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO),
307 NULL);
Franky Lin2bc78e12011-11-04 22:23:38 +0100308
309 /* clear reset and allow it to propagate throughout the core */
Franky Line13ce262012-05-04 18:27:35 -0700310 brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
311 SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700312 regdata = brcmf_sdio_regrl(sdiodev,
313 CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
314 NULL);
Franky Lin2bc78e12011-11-04 22:23:38 +0100315 udelay(1);
316
317 /* leave clock enabled */
Franky Line13ce262012-05-04 18:27:35 -0700318 brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
319 SSB_TMSLOW_CLOCK, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700320 regdata = brcmf_sdio_regrl(sdiodev,
321 CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
322 NULL);
Franky Lind77e70f2011-11-10 20:30:24 +0100323 udelay(1);
324}
325
326static void
327brcmf_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 */
Franky Line13ce262012-05-04 18:27:35 -0700339 brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
340 BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700341 regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
342 NULL);
Franky Line13ce262012-05-04 18:27:35 -0700343 brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
344 0, NULL);
Franky Lind77e70f2011-11-10 20:30:24 +0100345 udelay(1);
346
Franky Line13ce262012-05-04 18:27:35 -0700347 brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
348 BCMA_IOCTL_CLK, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700349 regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
350 NULL);
Franky Lin2bc78e12011-11-04 22:23:38 +0100351 udelay(1);
352}
353
Franky Lina83369b2011-11-04 22:23:28 +0100354static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev,
355 struct chip_info *ci, u32 regs)
356{
357 u32 regdata;
358
Franky Lin069eddd2013-04-11 13:28:48 +0200359 /* Get CC core rev
Franky Lina83369b2011-11-04 22:23:28 +0100360 * Chipid is assume to be at offset 0 from regs arg
361 * For different chiptypes or old sdio hosts w/o chipcommon,
362 * other ways of recognition should be added here.
363 */
Franky Lin99ba15c2011-11-04 22:23:42 +0100364 ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON;
365 ci->c_inf[0].base = regs;
Franky Lin79ae3952012-05-04 18:27:34 -0700366 regdata = brcmf_sdio_regrl(sdiodev,
367 CORE_CC_REG(ci->c_inf[0].base, chipid),
368 NULL);
Franky Lina83369b2011-11-04 22:23:28 +0100369 ci->chip = regdata & CID_ID_MASK;
370 ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
Franky Lin6ca687d2011-11-10 20:30:21 +0100371 ci->socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT;
Franky Lina83369b2011-11-04 22:23:28 +0100372
373 brcmf_dbg(INFO, "chipid=0x%x chiprev=%d\n", ci->chip, ci->chiprev);
374
375 /* Address of cores for new chips should be added here */
376 switch (ci->chip) {
Franky Lin4a1c02c2012-08-30 19:42:59 +0200377 case BCM43241_CHIP_ID:
378 ci->c_inf[0].wrapbase = 0x18100000;
379 ci->c_inf[0].cib = 0x2a084411;
380 ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
381 ci->c_inf[1].base = 0x18002000;
382 ci->c_inf[1].wrapbase = 0x18102000;
383 ci->c_inf[1].cib = 0x0e004211;
384 ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
385 ci->c_inf[2].base = 0x18004000;
386 ci->c_inf[2].wrapbase = 0x18104000;
387 ci->c_inf[2].cib = 0x14080401;
388 ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
389 ci->c_inf[3].base = 0x18003000;
390 ci->c_inf[3].wrapbase = 0x18103000;
391 ci->c_inf[3].cib = 0x07004211;
392 ci->ramsize = 0x90000;
393 break;
Franky Lina83369b2011-11-04 22:23:28 +0100394 case BCM4329_CHIP_ID:
Franky Lin99ba15c2011-11-04 22:23:42 +0100395 ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
396 ci->c_inf[1].base = BCM4329_CORE_BUS_BASE;
397 ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
398 ci->c_inf[2].base = BCM4329_CORE_SOCRAM_BASE;
399 ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
400 ci->c_inf[3].base = BCM4329_CORE_ARM_BASE;
Franky Lina83369b2011-11-04 22:23:28 +0100401 ci->ramsize = BCM4329_RAMSIZE;
402 break;
Franky Lince2d7d72011-12-08 15:06:39 -0800403 case BCM4330_CHIP_ID:
404 ci->c_inf[0].wrapbase = 0x18100000;
405 ci->c_inf[0].cib = 0x27004211;
406 ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
407 ci->c_inf[1].base = 0x18002000;
408 ci->c_inf[1].wrapbase = 0x18102000;
409 ci->c_inf[1].cib = 0x07004211;
410 ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
411 ci->c_inf[2].base = 0x18004000;
412 ci->c_inf[2].wrapbase = 0x18104000;
413 ci->c_inf[2].cib = 0x0d080401;
414 ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
415 ci->c_inf[3].base = 0x18003000;
416 ci->c_inf[3].wrapbase = 0x18103000;
417 ci->c_inf[3].cib = 0x03004211;
418 ci->ramsize = 0x48000;
419 break;
Franky Lin85a4a1c2012-06-26 21:26:39 +0200420 case BCM4334_CHIP_ID:
421 ci->c_inf[0].wrapbase = 0x18100000;
422 ci->c_inf[0].cib = 0x29004211;
423 ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
424 ci->c_inf[1].base = 0x18002000;
425 ci->c_inf[1].wrapbase = 0x18102000;
426 ci->c_inf[1].cib = 0x0d004211;
427 ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
428 ci->c_inf[2].base = 0x18004000;
429 ci->c_inf[2].wrapbase = 0x18104000;
430 ci->c_inf[2].cib = 0x13080401;
431 ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
432 ci->c_inf[3].base = 0x18003000;
433 ci->c_inf[3].wrapbase = 0x18103000;
434 ci->c_inf[3].cib = 0x07004211;
435 ci->ramsize = 0x80000;
436 break;
Franky Lina83369b2011-11-04 22:23:28 +0100437 default:
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100438 brcmf_err("chipid 0x%x is not supported\n", ci->chip);
Franky Lina83369b2011-11-04 22:23:28 +0100439 return -ENODEV;
440 }
441
Franky Lin6ca687d2011-11-10 20:30:21 +0100442 switch (ci->socitype) {
443 case SOCI_SB:
444 ci->iscoreup = brcmf_sdio_sb_iscoreup;
Franky Lin523894f2011-11-10 20:30:22 +0100445 ci->corerev = brcmf_sdio_sb_corerev;
Franky Lin086a2e02011-11-10 20:30:23 +0100446 ci->coredisable = brcmf_sdio_sb_coredisable;
Franky Lind77e70f2011-11-10 20:30:24 +0100447 ci->resetcore = brcmf_sdio_sb_resetcore;
Franky Lin6ca687d2011-11-10 20:30:21 +0100448 break;
449 case SOCI_AI:
450 ci->iscoreup = brcmf_sdio_ai_iscoreup;
Franky Lin523894f2011-11-10 20:30:22 +0100451 ci->corerev = brcmf_sdio_ai_corerev;
Franky Lin086a2e02011-11-10 20:30:23 +0100452 ci->coredisable = brcmf_sdio_ai_coredisable;
Franky Lind77e70f2011-11-10 20:30:24 +0100453 ci->resetcore = brcmf_sdio_ai_resetcore;
Franky Lin6ca687d2011-11-10 20:30:21 +0100454 break;
455 default:
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100456 brcmf_err("socitype %u not supported\n", ci->socitype);
Franky Lin6ca687d2011-11-10 20:30:21 +0100457 return -ENODEV;
458 }
459
Franky Lina83369b2011-11-04 22:23:28 +0100460 return 0;
461}
462
Franky Line63ac6b2011-11-04 22:23:29 +0100463static int
464brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev)
465{
466 int err = 0;
467 u8 clkval, clkset;
468
469 /* Try forcing SDIO core to do ALPAvail request only */
470 clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
Franky Lin3bba8292012-05-04 18:27:33 -0700471 brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
Franky Line63ac6b2011-11-04 22:23:29 +0100472 if (err) {
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100473 brcmf_err("error writing for HT off\n");
Franky Line63ac6b2011-11-04 22:23:29 +0100474 return err;
475 }
476
477 /* If register supported, wait for ALPAvail and then force ALP */
478 /* This may take up to 15 milliseconds */
Franky Lin45db3392012-05-04 18:27:32 -0700479 clkval = brcmf_sdio_regrb(sdiodev,
480 SBSDIO_FUNC1_CHIPCLKCSR, NULL);
Franky Line63ac6b2011-11-04 22:23:29 +0100481
482 if ((clkval & ~SBSDIO_AVBITS) != clkset) {
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100483 brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
Franky Line63ac6b2011-11-04 22:23:29 +0100484 clkset, clkval);
485 return -EACCES;
486 }
487
Franky Lin45db3392012-05-04 18:27:32 -0700488 SPINWAIT(((clkval = brcmf_sdio_regrb(sdiodev,
489 SBSDIO_FUNC1_CHIPCLKCSR, NULL)),
Franky Line63ac6b2011-11-04 22:23:29 +0100490 !SBSDIO_ALPAV(clkval)),
491 PMU_MAX_TRANSITION_DLY);
492 if (!SBSDIO_ALPAV(clkval)) {
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100493 brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n",
Franky Line63ac6b2011-11-04 22:23:29 +0100494 clkval);
495 return -EBUSY;
496 }
497
498 clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP;
Franky Lin3bba8292012-05-04 18:27:33 -0700499 brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
Franky Line63ac6b2011-11-04 22:23:29 +0100500 udelay(65);
501
502 /* Also, disable the extra SDIO pull-ups */
Franky Lin3bba8292012-05-04 18:27:33 -0700503 brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
Franky Line63ac6b2011-11-04 22:23:29 +0100504
505 return 0;
506}
507
Franky Lin5b45e542011-11-04 22:23:30 +0100508static void
509brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev,
510 struct chip_info *ci)
511{
Franky Lin79ae3952012-05-04 18:27:34 -0700512 u32 base = ci->c_inf[0].base;
513
Franky Lin5b45e542011-11-04 22:23:30 +0100514 /* get chipcommon rev */
Franky Lin523894f2011-11-10 20:30:22 +0100515 ci->c_inf[0].rev = ci->corerev(sdiodev, ci, ci->c_inf[0].id);
Franky Lin5b45e542011-11-04 22:23:30 +0100516
517 /* get chipcommon capabilites */
Franky Lin79ae3952012-05-04 18:27:34 -0700518 ci->c_inf[0].caps = brcmf_sdio_regrl(sdiodev,
519 CORE_CC_REG(base, capabilities),
520 NULL);
Franky Lin5b45e542011-11-04 22:23:30 +0100521
522 /* get pmu caps & rev */
Franky Lin99ba15c2011-11-04 22:23:42 +0100523 if (ci->c_inf[0].caps & CC_CAP_PMU) {
Franky Lin79ae3952012-05-04 18:27:34 -0700524 ci->pmucaps =
525 brcmf_sdio_regrl(sdiodev,
526 CORE_CC_REG(base, pmucapabilities),
527 NULL);
Franky Lin5b45e542011-11-04 22:23:30 +0100528 ci->pmurev = ci->pmucaps & PCAP_REV_MASK;
529 }
530
Franky Lin523894f2011-11-10 20:30:22 +0100531 ci->c_inf[1].rev = ci->corerev(sdiodev, ci, ci->c_inf[1].id);
Franky Lin5b45e542011-11-04 22:23:30 +0100532
533 brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n",
Franky Lin99ba15c2011-11-04 22:23:42 +0100534 ci->c_inf[0].rev, ci->pmurev,
535 ci->c_inf[1].rev, ci->c_inf[1].id);
Franky Lin966414d2011-11-04 22:23:32 +0100536
537 /*
538 * Make sure any on-chip ARM is off (in case strapping is wrong),
539 * or downloaded code was already running.
540 */
Franky Lin086a2e02011-11-10 20:30:23 +0100541 ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3);
Franky Lin5b45e542011-11-04 22:23:30 +0100542}
543
Franky Lina83369b2011-11-04 22:23:28 +0100544int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev,
Franky Lina97e4fc2011-11-04 22:23:35 +0100545 struct chip_info **ci_ptr, u32 regs)
Franky Lina83369b2011-11-04 22:23:28 +0100546{
Franky Lina97e4fc2011-11-04 22:23:35 +0100547 int ret;
548 struct chip_info *ci;
549
550 brcmf_dbg(TRACE, "Enter\n");
551
552 /* alloc chip_info_t */
553 ci = kzalloc(sizeof(struct chip_info), GFP_ATOMIC);
554 if (!ci)
555 return -ENOMEM;
Franky Lina83369b2011-11-04 22:23:28 +0100556
Franky Line63ac6b2011-11-04 22:23:29 +0100557 ret = brcmf_sdio_chip_buscoreprep(sdiodev);
558 if (ret != 0)
Franky Lina97e4fc2011-11-04 22:23:35 +0100559 goto err;
Franky Line63ac6b2011-11-04 22:23:29 +0100560
Franky Lina83369b2011-11-04 22:23:28 +0100561 ret = brcmf_sdio_chip_recognition(sdiodev, ci, regs);
562 if (ret != 0)
Franky Lina97e4fc2011-11-04 22:23:35 +0100563 goto err;
Franky Lina83369b2011-11-04 22:23:28 +0100564
Franky Lin5b45e542011-11-04 22:23:30 +0100565 brcmf_sdio_chip_buscoresetup(sdiodev, ci);
566
Franky Line13ce262012-05-04 18:27:35 -0700567 brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopullup),
568 0, NULL);
569 brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopulldown),
570 0, NULL);
Franky Lin960908d2011-11-04 22:23:33 +0100571
Franky Lina97e4fc2011-11-04 22:23:35 +0100572 *ci_ptr = ci;
573 return 0;
574
575err:
576 kfree(ci);
Franky Lina83369b2011-11-04 22:23:28 +0100577 return ret;
578}
Franky Lina8a6c042011-11-04 22:23:39 +0100579
580void
581brcmf_sdio_chip_detach(struct chip_info **ci_ptr)
582{
583 brcmf_dbg(TRACE, "Enter\n");
584
585 kfree(*ci_ptr);
586 *ci_ptr = NULL;
587}
Franky Line12afb62011-11-04 22:23:40 +0100588
589static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len)
590{
591 const char *fmt;
592
593 fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
594 snprintf(buf, len, fmt, chipid);
595 return buf;
596}
597
598void
599brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
600 struct chip_info *ci, u32 drivestrength)
601{
602 struct sdiod_drive_str *str_tab = NULL;
603 u32 str_mask = 0;
604 u32 str_shift = 0;
605 char chn[8];
Franky Lin79ae3952012-05-04 18:27:34 -0700606 u32 base = ci->c_inf[0].base;
Franky Line12afb62011-11-04 22:23:40 +0100607
Franky Lin99ba15c2011-11-04 22:23:42 +0100608 if (!(ci->c_inf[0].caps & CC_CAP_PMU))
Franky Line12afb62011-11-04 22:23:40 +0100609 return;
610
611 switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) {
Franky Lince2d7d72011-12-08 15:06:39 -0800612 case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12):
Franky Linffb27562011-12-08 15:06:40 -0800613 str_tab = (struct sdiod_drive_str *)&sdiod_drvstr_tab1_1v8;
Franky Lince2d7d72011-12-08 15:06:39 -0800614 str_mask = 0x00003800;
615 str_shift = 11;
616 break;
Franky Line12afb62011-11-04 22:23:40 +0100617 default:
Arend van Spriel5e8149f2012-12-07 10:49:57 +0100618 brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n",
Franky Line12afb62011-11-04 22:23:40 +0100619 brcmf_sdio_chip_name(ci->chip, chn, 8),
620 ci->chiprev, ci->pmurev);
621 break;
622 }
623
624 if (str_tab != NULL) {
625 u32 drivestrength_sel = 0;
626 u32 cc_data_temp;
627 int i;
628
629 for (i = 0; str_tab[i].strength != 0; i++) {
630 if (drivestrength >= str_tab[i].strength) {
631 drivestrength_sel = str_tab[i].sel;
632 break;
633 }
634 }
635
Franky Line13ce262012-05-04 18:27:35 -0700636 brcmf_sdio_regwl(sdiodev, CORE_CC_REG(base, chipcontrol_addr),
637 1, NULL);
Franky Lin79ae3952012-05-04 18:27:34 -0700638 cc_data_temp =
639 brcmf_sdio_regrl(sdiodev,
640 CORE_CC_REG(base, chipcontrol_addr),
641 NULL);
Franky Line12afb62011-11-04 22:23:40 +0100642 cc_data_temp &= ~str_mask;
643 drivestrength_sel <<= str_shift;
644 cc_data_temp |= drivestrength_sel;
Franky Line13ce262012-05-04 18:27:35 -0700645 brcmf_sdio_regwl(sdiodev, CORE_CC_REG(base, chipcontrol_addr),
646 cc_data_temp, NULL);
Franky Line12afb62011-11-04 22:23:40 +0100647
648 brcmf_dbg(INFO, "SDIO: %dmA drive strength selected, set to 0x%08x\n",
649 drivestrength, cc_data_temp);
650 }
651}
Franky Lin069eddd2013-04-11 13:28:48 +0200652
653#ifdef DEBUG
654static bool
655brcmf_sdio_chip_verifynvram(struct brcmf_sdio_dev *sdiodev, u32 nvram_addr,
656 char *nvram_dat, uint nvram_sz)
657{
658 char *nvram_ularray;
659 int err;
660 bool ret = true;
661
662 /* read back and verify */
663 brcmf_dbg(INFO, "Compare NVRAM dl & ul; size=%d\n", nvram_sz);
664 nvram_ularray = kmalloc(nvram_sz, GFP_KERNEL);
665 /* do not proceed while no memory but */
666 if (!nvram_ularray)
667 return true;
668
669 /* Upload image to verify downloaded contents. */
670 memset(nvram_ularray, 0xaa, nvram_sz);
671
672 /* Read the vars list to temp buffer for comparison */
673 err = brcmf_sdio_ramrw(sdiodev, false, nvram_addr, nvram_ularray,
674 nvram_sz);
675 if (err) {
676 brcmf_err("error %d on reading %d nvram bytes at 0x%08x\n",
677 err, nvram_sz, nvram_addr);
678 } else if (memcmp(nvram_dat, nvram_ularray, nvram_sz)) {
679 brcmf_err("Downloaded NVRAM image is corrupted\n");
680 ret = false;
681 }
682 kfree(nvram_ularray);
683
684 return ret;
685}
686#else /* DEBUG */
687static inline bool
688brcmf_sdio_chip_verifynvram(struct brcmf_sdio_dev *sdiodev, u32 nvram_addr,
689 char *nvram_dat, uint nvram_sz)
690{
691 return true;
692}
693#endif /* DEBUG */
694
695static bool brcmf_sdio_chip_writenvram(struct brcmf_sdio_dev *sdiodev,
696 struct chip_info *ci,
697 char *nvram_dat, uint nvram_sz)
698{
699 int err;
700 u32 nvram_addr;
701 u32 token;
702 __le32 token_le;
703
704 nvram_addr = (ci->ramsize - 4) - nvram_sz;
705
706 /* Write the vars list */
707 err = brcmf_sdio_ramrw(sdiodev, true, nvram_addr, nvram_dat, nvram_sz);
708 if (err) {
709 brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n",
710 err, nvram_sz, nvram_addr);
711 return false;
712 }
713
714 if (!brcmf_sdio_chip_verifynvram(sdiodev, nvram_addr,
715 nvram_dat, nvram_sz))
716 return false;
717
718 /* generate token:
719 * nvram size, converted to words, in lower 16-bits, checksum
720 * in upper 16-bits.
721 */
722 token = nvram_sz / 4;
723 token = (~token << 16) | (token & 0x0000FFFF);
724 token_le = cpu_to_le32(token);
725
726 brcmf_dbg(INFO, "RAM size: %d\n", ci->ramsize);
727 brcmf_dbg(INFO, "nvram is placed at %d, size %d, token=0x%08x\n",
728 nvram_addr, nvram_sz, token);
729
730 /* Write the length token to the last word */
731 if (brcmf_sdio_ramrw(sdiodev, true, (ci->ramsize - 4),
732 (u8 *)&token_le, 4))
733 return false;
734
735 return true;
736}
737
738static void
739brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev,
740 struct chip_info *ci)
741{
742 u32 zeros = 0;
743
744 ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3);
745 ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM);
746
747 /* clear length token */
748 brcmf_sdio_ramrw(sdiodev, true, ci->ramsize - 4, (u8 *)&zeros, 4);
749}
750
751static bool
752brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci,
753 char *nvram_dat, uint nvram_sz)
754{
755 u8 core_idx;
756 u32 reg_addr;
757
758 if (!ci->iscoreup(sdiodev, ci, BCMA_CORE_INTERNAL_MEM)) {
759 brcmf_err("SOCRAM core is down after reset?\n");
760 return false;
761 }
762
763 if (!brcmf_sdio_chip_writenvram(sdiodev, ci, nvram_dat, nvram_sz))
764 return false;
765
766 /* clear all interrupts */
767 core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV);
768 reg_addr = ci->c_inf[core_idx].base;
769 reg_addr += offsetof(struct sdpcmd_regs, intstatus);
770 brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
771
772 ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3);
773
774 return true;
775}
776
777void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev,
778 struct chip_info *ci)
779{
780 brcmf_sdio_chip_cm3_enterdl(sdiodev, ci);
781}
782
783bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev,
784 struct chip_info *ci, char *nvram_dat,
785 uint nvram_sz)
786{
787 return brcmf_sdio_chip_cm3_exitdl(sdiodev, ci, nvram_dat, nvram_sz);
788}