diff options
author | blogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-10-05 10:12:53 +0000 |
---|---|---|
committer | blogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-10-05 10:12:53 +0000 |
commit | 5c105d9f3fd086aff195d3849dcf847d6b0bd927 (patch) | |
tree | 1229a11f725bfa58aa7c57a76898553bb5f6654a /target/linux/brcm47xx/patches-3.3 | |
download | openwrt-5c105d9f3fd086aff195d3849dcf847d6b0bd927.tar.gz openwrt-5c105d9f3fd086aff195d3849dcf847d6b0bd927.zip |
branch Attitude Adjustment
git-svn-id: svn://svn.openwrt.org/openwrt/branches/attitude_adjustment@33625 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/brcm47xx/patches-3.3')
64 files changed, 11241 insertions, 0 deletions
diff --git a/target/linux/brcm47xx/patches-3.3/001-MIPS-BCM47XX-return-number-of-written-bytes-in-nvram.patch b/target/linux/brcm47xx/patches-3.3/001-MIPS-BCM47XX-return-number-of-written-bytes-in-nvram.patch new file mode 100644 index 000000000..4fb7c309c --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/001-MIPS-BCM47XX-return-number-of-written-bytes-in-nvram.patch @@ -0,0 +1,12 @@ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -107,8 +107,7 @@ int nvram_getenv(char *name, char *val, + value = eq + 1; + if ((eq - var) == strlen(name) && + strncmp(var, name, (eq - var)) == 0) { +- snprintf(val, val_len, "%s", value); +- return 0; ++ return snprintf(val, val_len, "%s", value); + } + } + return NVRAM_ERR_ENVNOTFOUND; diff --git a/target/linux/brcm47xx/patches-3.3/002-MIPS-BCM47XX-fix-signature-of-nvram_parse_macaddr.patch b/target/linux/brcm47xx/patches-3.3/002-MIPS-BCM47XX-fix-signature-of-nvram_parse_macaddr.patch new file mode 100644 index 000000000..1191a3c7e --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/002-MIPS-BCM47XX-fix-signature-of-nvram_parse_macaddr.patch @@ -0,0 +1,11 @@ +--- a/arch/mips/include/asm/mach-bcm47xx/nvram.h ++++ b/arch/mips/include/asm/mach-bcm47xx/nvram.h +@@ -37,7 +37,7 @@ struct nvram_header { + + extern int nvram_getenv(char *name, char *val, size_t val_len); + +-static inline void nvram_parse_macaddr(char *buf, u8 *macaddr) ++static inline void nvram_parse_macaddr(char *buf, u8 macaddr[6]) + { + if (strchr(buf, ':')) + sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &macaddr[0], diff --git a/target/linux/brcm47xx/patches-3.3/003-MIPS-BCM47XX-move-and-extend-sprom-parsing.patch b/target/linux/brcm47xx/patches-3.3/003-MIPS-BCM47XX-move-and-extend-sprom-parsing.patch new file mode 100644 index 000000000..9a83015f6 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/003-MIPS-BCM47XX-move-and-extend-sprom-parsing.patch @@ -0,0 +1,802 @@ +--- a/arch/mips/bcm47xx/Makefile ++++ b/arch/mips/bcm47xx/Makefile +@@ -3,5 +3,5 @@ + # under Linux. + # + +-obj-y += gpio.o irq.o nvram.o prom.o serial.o setup.o time.o ++obj-y += gpio.o irq.o nvram.o prom.o serial.o setup.o time.o sprom.o + obj-$(CONFIG_BCM47XX_SSB) += wgt634u.o +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -85,156 +85,7 @@ static void bcm47xx_machine_halt(void) + } + + #ifdef CONFIG_BCM47XX_SSB +-#define READ_FROM_NVRAM(_outvar, name, buf) \ +- if (nvram_getprefix(prefix, name, buf, sizeof(buf)) >= 0)\ +- sprom->_outvar = simple_strtoul(buf, NULL, 0); +- +-#define READ_FROM_NVRAM2(_outvar, name1, name2, buf) \ +- if (nvram_getprefix(prefix, name1, buf, sizeof(buf)) >= 0 || \ +- nvram_getprefix(prefix, name2, buf, sizeof(buf)) >= 0)\ +- sprom->_outvar = simple_strtoul(buf, NULL, 0); +- +-static inline int nvram_getprefix(const char *prefix, char *name, +- char *buf, int len) +-{ +- if (prefix) { +- char key[100]; +- +- snprintf(key, sizeof(key), "%s%s", prefix, name); +- return nvram_getenv(key, buf, len); +- } +- +- return nvram_getenv(name, buf, len); +-} +- +-static u32 nvram_getu32(const char *name, char *buf, int len) +-{ +- int rv; +- char key[100]; +- u16 var0, var1; +- +- snprintf(key, sizeof(key), "%s0", name); +- rv = nvram_getenv(key, buf, len); +- /* return 0 here so this looks like unset */ +- if (rv < 0) +- return 0; +- var0 = simple_strtoul(buf, NULL, 0); +- +- snprintf(key, sizeof(key), "%s1", name); +- rv = nvram_getenv(key, buf, len); +- if (rv < 0) +- return 0; +- var1 = simple_strtoul(buf, NULL, 0); +- return var1 << 16 | var0; +-} +- +-static void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix) +-{ +- char buf[100]; +- u32 boardflags; +- +- memset(sprom, 0, sizeof(struct ssb_sprom)); +- +- sprom->revision = 1; /* Fallback: Old hardware does not define this. */ +- READ_FROM_NVRAM(revision, "sromrev", buf); +- if (nvram_getprefix(prefix, "il0macaddr", buf, sizeof(buf)) >= 0 || +- nvram_getprefix(prefix, "macaddr", buf, sizeof(buf)) >= 0) +- nvram_parse_macaddr(buf, sprom->il0mac); +- if (nvram_getprefix(prefix, "et0macaddr", buf, sizeof(buf)) >= 0) +- nvram_parse_macaddr(buf, sprom->et0mac); +- if (nvram_getprefix(prefix, "et1macaddr", buf, sizeof(buf)) >= 0) +- nvram_parse_macaddr(buf, sprom->et1mac); +- READ_FROM_NVRAM(et0phyaddr, "et0phyaddr", buf); +- READ_FROM_NVRAM(et1phyaddr, "et1phyaddr", buf); +- READ_FROM_NVRAM(et0mdcport, "et0mdcport", buf); +- READ_FROM_NVRAM(et1mdcport, "et1mdcport", buf); +- READ_FROM_NVRAM(board_rev, "boardrev", buf); +- READ_FROM_NVRAM(country_code, "ccode", buf); +- READ_FROM_NVRAM(ant_available_a, "aa5g", buf); +- READ_FROM_NVRAM(ant_available_bg, "aa2g", buf); +- READ_FROM_NVRAM(pa0b0, "pa0b0", buf); +- READ_FROM_NVRAM(pa0b1, "pa0b1", buf); +- READ_FROM_NVRAM(pa0b2, "pa0b2", buf); +- READ_FROM_NVRAM(pa1b0, "pa1b0", buf); +- READ_FROM_NVRAM(pa1b1, "pa1b1", buf); +- READ_FROM_NVRAM(pa1b2, "pa1b2", buf); +- READ_FROM_NVRAM(pa1lob0, "pa1lob0", buf); +- READ_FROM_NVRAM(pa1lob2, "pa1lob1", buf); +- READ_FROM_NVRAM(pa1lob1, "pa1lob2", buf); +- READ_FROM_NVRAM(pa1hib0, "pa1hib0", buf); +- READ_FROM_NVRAM(pa1hib2, "pa1hib1", buf); +- READ_FROM_NVRAM(pa1hib1, "pa1hib2", buf); +- READ_FROM_NVRAM2(gpio0, "ledbh0", "wl0gpio0", buf); +- READ_FROM_NVRAM2(gpio1, "ledbh1", "wl0gpio1", buf); +- READ_FROM_NVRAM2(gpio2, "ledbh2", "wl0gpio2", buf); +- READ_FROM_NVRAM2(gpio3, "ledbh3", "wl0gpio3", buf); +- READ_FROM_NVRAM2(maxpwr_bg, "maxp2ga0", "pa0maxpwr", buf); +- READ_FROM_NVRAM2(maxpwr_al, "maxp5gla0", "pa1lomaxpwr", buf); +- READ_FROM_NVRAM2(maxpwr_a, "maxp5ga0", "pa1maxpwr", buf); +- READ_FROM_NVRAM2(maxpwr_ah, "maxp5gha0", "pa1himaxpwr", buf); +- READ_FROM_NVRAM2(itssi_bg, "itt5ga0", "pa0itssit", buf); +- READ_FROM_NVRAM2(itssi_a, "itt2ga0", "pa1itssit", buf); +- READ_FROM_NVRAM(tri2g, "tri2g", buf); +- READ_FROM_NVRAM(tri5gl, "tri5gl", buf); +- READ_FROM_NVRAM(tri5g, "tri5g", buf); +- READ_FROM_NVRAM(tri5gh, "tri5gh", buf); +- READ_FROM_NVRAM(txpid2g[0], "txpid2ga0", buf); +- READ_FROM_NVRAM(txpid2g[1], "txpid2ga1", buf); +- READ_FROM_NVRAM(txpid2g[2], "txpid2ga2", buf); +- READ_FROM_NVRAM(txpid2g[3], "txpid2ga3", buf); +- READ_FROM_NVRAM(txpid5g[0], "txpid5ga0", buf); +- READ_FROM_NVRAM(txpid5g[1], "txpid5ga1", buf); +- READ_FROM_NVRAM(txpid5g[2], "txpid5ga2", buf); +- READ_FROM_NVRAM(txpid5g[3], "txpid5ga3", buf); +- READ_FROM_NVRAM(txpid5gl[0], "txpid5gla0", buf); +- READ_FROM_NVRAM(txpid5gl[1], "txpid5gla1", buf); +- READ_FROM_NVRAM(txpid5gl[2], "txpid5gla2", buf); +- READ_FROM_NVRAM(txpid5gl[3], "txpid5gla3", buf); +- READ_FROM_NVRAM(txpid5gh[0], "txpid5gha0", buf); +- READ_FROM_NVRAM(txpid5gh[1], "txpid5gha1", buf); +- READ_FROM_NVRAM(txpid5gh[2], "txpid5gha2", buf); +- READ_FROM_NVRAM(txpid5gh[3], "txpid5gha3", buf); +- READ_FROM_NVRAM(rxpo2g, "rxpo2g", buf); +- READ_FROM_NVRAM(rxpo5g, "rxpo5g", buf); +- READ_FROM_NVRAM(rssisav2g, "rssisav2g", buf); +- READ_FROM_NVRAM(rssismc2g, "rssismc2g", buf); +- READ_FROM_NVRAM(rssismf2g, "rssismf2g", buf); +- READ_FROM_NVRAM(bxa2g, "bxa2g", buf); +- READ_FROM_NVRAM(rssisav5g, "rssisav5g", buf); +- READ_FROM_NVRAM(rssismc5g, "rssismc5g", buf); +- READ_FROM_NVRAM(rssismf5g, "rssismf5g", buf); +- READ_FROM_NVRAM(bxa5g, "bxa5g", buf); +- READ_FROM_NVRAM(cck2gpo, "cck2gpo", buf); +- +- sprom->ofdm2gpo = nvram_getu32("ofdm2gpo", buf, sizeof(buf)); +- sprom->ofdm5glpo = nvram_getu32("ofdm5glpo", buf, sizeof(buf)); +- sprom->ofdm5gpo = nvram_getu32("ofdm5gpo", buf, sizeof(buf)); +- sprom->ofdm5ghpo = nvram_getu32("ofdm5ghpo", buf, sizeof(buf)); +- +- READ_FROM_NVRAM(antenna_gain.ghz24.a0, "ag0", buf); +- READ_FROM_NVRAM(antenna_gain.ghz24.a1, "ag1", buf); +- READ_FROM_NVRAM(antenna_gain.ghz24.a2, "ag2", buf); +- READ_FROM_NVRAM(antenna_gain.ghz24.a3, "ag3", buf); +- memcpy(&sprom->antenna_gain.ghz5, &sprom->antenna_gain.ghz24, +- sizeof(sprom->antenna_gain.ghz5)); +- +- if (nvram_getprefix(prefix, "boardflags", buf, sizeof(buf)) >= 0) { +- boardflags = simple_strtoul(buf, NULL, 0); +- if (boardflags) { +- sprom->boardflags_lo = (boardflags & 0x0000FFFFU); +- sprom->boardflags_hi = (boardflags & 0xFFFF0000U) >> 16; +- } +- } +- if (nvram_getprefix(prefix, "boardflags2", buf, sizeof(buf)) >= 0) { +- boardflags = simple_strtoul(buf, NULL, 0); +- if (boardflags) { +- sprom->boardflags2_lo = (boardflags & 0x0000FFFFU); +- sprom->boardflags2_hi = (boardflags & 0xFFFF0000U) >> 16; +- } +- } +-} +- +-int bcm47xx_get_sprom(struct ssb_bus *bus, struct ssb_sprom *out) ++static int bcm47xx_get_sprom(struct ssb_bus *bus, struct ssb_sprom *out) + { + char prefix[10]; + +--- /dev/null ++++ b/arch/mips/bcm47xx/sprom.c +@@ -0,0 +1,620 @@ ++/* ++ * Copyright (C) 2004 Florian Schirmer <jolt@tuxbox.org> ++ * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> ++ * Copyright (C) 2006 Michael Buesch <m@bues.ch> ++ * Copyright (C) 2010 Waldemar Brodkorb <wbx@openadk.org> ++ * Copyright (C) 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN ++ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ++ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ++ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include <bcm47xx.h> ++#include <nvram.h> ++ ++static void create_key(const char *prefix, const char *postfix, ++ const char *name, char *buf, int len) ++{ ++ if (prefix && postfix) ++ snprintf(buf, len, "%s%s%s", prefix, name, postfix); ++ else if (prefix) ++ snprintf(buf, len, "%s%s", prefix, name); ++ else if (postfix) ++ snprintf(buf, len, "%s%s", name, postfix); ++ else ++ snprintf(buf, len, "%s", name); ++} ++ ++#define NVRAM_READ_VAL(type) \ ++static void nvram_read_ ## type (const char *prefix, \ ++ const char *postfix, const char *name, \ ++ type *val, type allset) \ ++{ \ ++ char buf[100]; \ ++ char key[40]; \ ++ int err; \ ++ type var; \ ++ \ ++ create_key(prefix, postfix, name, key, sizeof(key)); \ ++ \ ++ err = nvram_getenv(key, buf, sizeof(buf)); \ ++ if (err < 0) \ ++ return; \ ++ err = kstrto ## type (buf, 0, &var); \ ++ if (err) { \ ++ pr_warn("can not parse nvram name %s with value %s" \ ++ " got %i", key, buf, err); \ ++ return; \ ++ } \ ++ if (allset && var == allset) \ ++ return; \ ++ *val = var; \ ++} ++ ++NVRAM_READ_VAL(u8) ++NVRAM_READ_VAL(s8) ++NVRAM_READ_VAL(u16) ++NVRAM_READ_VAL(u32) ++ ++#undef NVRAM_READ_VAL ++ ++static void nvram_read_u32_2(const char *prefix, const char *name, ++ u16 *val_lo, u16 *val_hi) ++{ ++ char buf[100]; ++ char key[40]; ++ int err; ++ u32 val; ++ ++ create_key(prefix, NULL, name, key, sizeof(key)); ++ ++ err = nvram_getenv(key, buf, sizeof(buf)); ++ if (err < 0) ++ return; ++ err = kstrtou32(buf, 0, &val); ++ if (err) { ++ pr_warn("can not parse nvram name %s with value %s got %i", ++ key, buf, err); ++ return; ++ } ++ *val_lo = (val & 0x0000FFFFU); ++ *val_hi = (val & 0xFFFF0000U) >> 16; ++} ++ ++static void nvram_read_leddc(const char *prefix, const char *name, ++ u8 *leddc_on_time, u8 *leddc_off_time) ++{ ++ char buf[100]; ++ char key[40]; ++ int err; ++ u32 val; ++ ++ create_key(prefix, NULL, name, key, sizeof(key)); ++ ++ err = nvram_getenv(key, buf, sizeof(buf)); ++ if (err < 0) ++ return; ++ err = kstrtou32(buf, 0, &val); ++ if (err) { ++ pr_warn("can not parse nvram name %s with value %s got %i", ++ key, buf, err); ++ return; ++ } ++ ++ if (val == 0xffff || val == 0xffffffff) ++ return; ++ ++ *leddc_on_time = val & 0xff; ++ *leddc_off_time = (val >> 16) & 0xff; ++} ++ ++static void nvram_read_macaddr(const char *prefix, const char *name, ++ u8 (*val)[6]) ++{ ++ char buf[100]; ++ char key[40]; ++ int err; ++ ++ create_key(prefix, NULL, name, key, sizeof(key)); ++ ++ err = nvram_getenv(key, buf, sizeof(buf)); ++ if (err < 0) ++ return; ++ nvram_parse_macaddr(buf, *val); ++} ++ ++static void nvram_read_alpha2(const char *prefix, const char *name, ++ char (*val)[2]) ++{ ++ char buf[10]; ++ char key[40]; ++ int err; ++ ++ create_key(prefix, NULL, name, key, sizeof(key)); ++ ++ err = nvram_getenv(key, buf, sizeof(buf)); ++ if (err < 0) ++ return; ++ if (buf[0] == '0') ++ return; ++ if (strlen(buf) > 2) { ++ pr_warn("alpha2 is too long %s", buf); ++ return; ++ } ++ memcpy(val, buf, sizeof(val)); ++} ++ ++static void bcm47xx_fill_sprom_r1234589(struct ssb_sprom *sprom, ++ const char *prefix) ++{ ++ nvram_read_u16(prefix, NULL, "boardrev", &sprom->board_rev, 0); ++ nvram_read_u16(prefix, NULL, "boardnum", &sprom->board_num, 0); ++ nvram_read_u8(prefix, NULL, "ledbh0", &sprom->gpio0, 0xff); ++ nvram_read_u8(prefix, NULL, "ledbh1", &sprom->gpio1, 0xff); ++ nvram_read_u8(prefix, NULL, "ledbh2", &sprom->gpio2, 0xff); ++ nvram_read_u8(prefix, NULL, "ledbh3", &sprom->gpio3, 0xff); ++ nvram_read_u8(prefix, NULL, "aa2g", &sprom->ant_available_bg, 0); ++ nvram_read_u8(prefix, NULL, "aa5g", &sprom->ant_available_a, 0); ++ nvram_read_s8(prefix, NULL, "ag0", &sprom->antenna_gain.a0, 0); ++ nvram_read_s8(prefix, NULL, "ag1", &sprom->antenna_gain.a1, 0); ++ nvram_read_alpha2(prefix, "ccode", &sprom->alpha2); ++} ++ ++static void bcm47xx_fill_sprom_r12389(struct ssb_sprom *sprom, ++ const char *prefix) ++{ ++ nvram_read_u16(prefix, NULL, "pa0b0", &sprom->pa0b0, 0); ++ nvram_read_u16(prefix, NULL, "pa0b1", &sprom->pa0b1, 0); ++ nvram_read_u16(prefix, NULL, "pa0b2", &sprom->pa0b2, 0); ++ nvram_read_u8(prefix, NULL, "pa0itssit", &sprom->itssi_bg, 0); ++ nvram_read_u8(prefix, NULL, "pa0maxpwr", &sprom->maxpwr_bg, 0); ++ nvram_read_u16(prefix, NULL, "pa1b0", &sprom->pa1b0, 0); ++ nvram_read_u16(prefix, NULL, "pa1b1", &sprom->pa1b1, 0); ++ nvram_read_u16(prefix, NULL, "pa1b2", &sprom->pa1b2, 0); ++ nvram_read_u8(prefix, NULL, "pa1itssit", &sprom->itssi_a, 0); ++ nvram_read_u8(prefix, NULL, "pa1maxpwr", &sprom->maxpwr_a, 0); ++} ++ ++static void bcm47xx_fill_sprom_r1(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u16(prefix, NULL, "boardflags", &sprom->boardflags_lo, 0); ++ nvram_read_u8(prefix, NULL, "cc", &sprom->country_code, 0); ++} ++ ++static void bcm47xx_fill_sprom_r2389(struct ssb_sprom *sprom, ++ const char *prefix) ++{ ++ nvram_read_u8(prefix, NULL, "opo", &sprom->opo, 0); ++ nvram_read_u16(prefix, NULL, "pa1lob0", &sprom->pa1lob0, 0); ++ nvram_read_u16(prefix, NULL, "pa1lob1", &sprom->pa1lob1, 0); ++ nvram_read_u16(prefix, NULL, "pa1lob2", &sprom->pa1lob2, 0); ++ nvram_read_u16(prefix, NULL, "pa1hib0", &sprom->pa1hib0, 0); ++ nvram_read_u16(prefix, NULL, "pa1hib1", &sprom->pa1hib1, 0); ++ nvram_read_u16(prefix, NULL, "pa1hib2", &sprom->pa1hib2, 0); ++ nvram_read_u8(prefix, NULL, "pa1lomaxpwr", &sprom->maxpwr_al, 0); ++ nvram_read_u8(prefix, NULL, "pa1himaxpwr", &sprom->maxpwr_ah, 0); ++} ++ ++static void bcm47xx_fill_sprom_r2(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u32_2(prefix, "boardflags", &sprom->boardflags_lo, ++ &sprom->boardflags_hi); ++ nvram_read_u16(prefix, NULL, "boardtype", &sprom->board_type, 0); ++} ++ ++static void bcm47xx_fill_sprom_r389(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u8(prefix, NULL, "bxa2g", &sprom->bxa2g, 0); ++ nvram_read_u8(prefix, NULL, "rssisav2g", &sprom->rssisav2g, 0); ++ nvram_read_u8(prefix, NULL, "rssismc2g", &sprom->rssismc2g, 0); ++ nvram_read_u8(prefix, NULL, "rssismf2g", &sprom->rssismf2g, 0); ++ nvram_read_u8(prefix, NULL, "bxa5g", &sprom->bxa5g, 0); ++ nvram_read_u8(prefix, NULL, "rssisav5g", &sprom->rssisav5g, 0); ++ nvram_read_u8(prefix, NULL, "rssismc5g", &sprom->rssismc5g, 0); ++ nvram_read_u8(prefix, NULL, "rssismf5g", &sprom->rssismf5g, 0); ++ nvram_read_u8(prefix, NULL, "tri2g", &sprom->tri2g, 0); ++ nvram_read_u8(prefix, NULL, "tri5g", &sprom->tri5g, 0); ++ nvram_read_u8(prefix, NULL, "tri5gl", &sprom->tri5gl, 0); ++ nvram_read_u8(prefix, NULL, "tri5gh", &sprom->tri5gh, 0); ++ nvram_read_s8(prefix, NULL, "rxpo2g", &sprom->rxpo2g, 0); ++ nvram_read_s8(prefix, NULL, "rxpo5g", &sprom->rxpo5g, 0); ++} ++ ++static void bcm47xx_fill_sprom_r3(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u32_2(prefix, "boardflags", &sprom->boardflags_lo, ++ &sprom->boardflags_hi); ++ nvram_read_u16(prefix, NULL, "boardtype", &sprom->board_type, 0); ++ nvram_read_u8(prefix, NULL, "regrev", &sprom->regrev, 0); ++ nvram_read_leddc(prefix, "leddc", &sprom->leddc_on_time, ++ &sprom->leddc_off_time); ++} ++ ++static void bcm47xx_fill_sprom_r4589(struct ssb_sprom *sprom, ++ const char *prefix) ++{ ++ nvram_read_u32_2(prefix, "boardflags", &sprom->boardflags_lo, ++ &sprom->boardflags_hi); ++ nvram_read_u32_2(prefix, "boardflags2", &sprom->boardflags2_lo, ++ &sprom->boardflags2_hi); ++ nvram_read_u16(prefix, NULL, "boardtype", &sprom->board_type, 0); ++ nvram_read_u8(prefix, NULL, "regrev", &sprom->regrev, 0); ++ nvram_read_s8(prefix, NULL, "ag2", &sprom->antenna_gain.a2, 0); ++ nvram_read_s8(prefix, NULL, "ag3", &sprom->antenna_gain.a3, 0); ++ nvram_read_u8(prefix, NULL, "txchain", &sprom->txchain, 0xf); ++ nvram_read_u8(prefix, NULL, "rxchain", &sprom->rxchain, 0xf); ++ nvram_read_u8(prefix, NULL, "antswitch", &sprom->antswitch, 0xff); ++ nvram_read_leddc(prefix, "leddc", &sprom->leddc_on_time, ++ &sprom->leddc_off_time); ++} ++ ++static void bcm47xx_fill_sprom_r458(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u16(prefix, NULL, "cck2gpo", &sprom->cck2gpo, 0); ++ nvram_read_u32(prefix, NULL, "ofdm2gpo", &sprom->ofdm2gpo, 0); ++ nvram_read_u32(prefix, NULL, "ofdm5gpo", &sprom->ofdm5gpo, 0); ++ nvram_read_u32(prefix, NULL, "ofdm5glpo", &sprom->ofdm5glpo, 0); ++ nvram_read_u32(prefix, NULL, "ofdm5ghpo", &sprom->ofdm5ghpo, 0); ++ nvram_read_u16(prefix, NULL, "cddpo", &sprom->cddpo, 0); ++ nvram_read_u16(prefix, NULL, "stbcpo", &sprom->stbcpo, 0); ++ nvram_read_u16(prefix, NULL, "bw40po", &sprom->bw40po, 0); ++ nvram_read_u16(prefix, NULL, "bwduppo", &sprom->bwduppo, 0); ++ nvram_read_u16(prefix, NULL, "mcs2gpo0", &sprom->mcs2gpo[0], 0); ++ nvram_read_u16(prefix, NULL, "mcs2gpo1", &sprom->mcs2gpo[1], 0); ++ nvram_read_u16(prefix, NULL, "mcs2gpo2", &sprom->mcs2gpo[2], 0); ++ nvram_read_u16(prefix, NULL, "mcs2gpo3", &sprom->mcs2gpo[3], 0); ++ nvram_read_u16(prefix, NULL, "mcs2gpo4", &sprom->mcs2gpo[4], 0); ++ nvram_read_u16(prefix, NULL, "mcs2gpo5", &sprom->mcs2gpo[5], 0); ++ nvram_read_u16(prefix, NULL, "mcs2gpo6", &sprom->mcs2gpo[6], 0); ++ nvram_read_u16(prefix, NULL, "mcs2gpo7", &sprom->mcs2gpo[7], 0); ++ nvram_read_u16(prefix, NULL, "mcs5gpo0", &sprom->mcs5gpo[0], 0); ++ nvram_read_u16(prefix, NULL, "mcs5gpo1", &sprom->mcs5gpo[1], 0); ++ nvram_read_u16(prefix, NULL, "mcs5gpo2", &sprom->mcs5gpo[2], 0); ++ nvram_read_u16(prefix, NULL, "mcs5gpo3", &sprom->mcs5gpo[3], 0); ++ nvram_read_u16(prefix, NULL, "mcs5gpo4", &sprom->mcs5gpo[4], 0); ++ nvram_read_u16(prefix, NULL, "mcs5gpo5", &sprom->mcs5gpo[5], 0); ++ nvram_read_u16(prefix, NULL, "mcs5gpo6", &sprom->mcs5gpo[6], 0); ++ nvram_read_u16(prefix, NULL, "mcs5gpo7", &sprom->mcs5gpo[7], 0); ++ nvram_read_u16(prefix, NULL, "mcs5glpo0", &sprom->mcs5glpo[0], 0); ++ nvram_read_u16(prefix, NULL, "mcs5glpo1", &sprom->mcs5glpo[1], 0); ++ nvram_read_u16(prefix, NULL, "mcs5glpo2", &sprom->mcs5glpo[2], 0); ++ nvram_read_u16(prefix, NULL, "mcs5glpo3", &sprom->mcs5glpo[3], 0); ++ nvram_read_u16(prefix, NULL, "mcs5glpo4", &sprom->mcs5glpo[4], 0); ++ nvram_read_u16(prefix, NULL, "mcs5glpo5", &sprom->mcs5glpo[5], 0); ++ nvram_read_u16(prefix, NULL, "mcs5glpo6", &sprom->mcs5glpo[6], 0); ++ nvram_read_u16(prefix, NULL, "mcs5glpo7", &sprom->mcs5glpo[7], 0); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo0", &sprom->mcs5ghpo[0], 0); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo1", &sprom->mcs5ghpo[1], 0); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo2", &sprom->mcs5ghpo[2], 0); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo3", &sprom->mcs5ghpo[3], 0); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo4", &sprom->mcs5ghpo[4], 0); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo5", &sprom->mcs5ghpo[5], 0); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo6", &sprom->mcs5ghpo[6], 0); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo7", &sprom->mcs5ghpo[7], 0); ++} ++ ++static void bcm47xx_fill_sprom_r45(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u8(prefix, NULL, "txpid2ga0", &sprom->txpid2g[0], 0); ++ nvram_read_u8(prefix, NULL, "txpid2ga1", &sprom->txpid2g[1], 0); ++ nvram_read_u8(prefix, NULL, "txpid2ga2", &sprom->txpid2g[2], 0); ++ nvram_read_u8(prefix, NULL, "txpid2ga3", &sprom->txpid2g[3], 0); ++ nvram_read_u8(prefix, NULL, "txpid5ga0", &sprom->txpid5g[0], 0); ++ nvram_read_u8(prefix, NULL, "txpid5ga1", &sprom->txpid5g[1], 0); ++ nvram_read_u8(prefix, NULL, "txpid5ga2", &sprom->txpid5g[2], 0); ++ nvram_read_u8(prefix, NULL, "txpid5ga3", &sprom->txpid5g[3], 0); ++ nvram_read_u8(prefix, NULL, "txpid5gla0", &sprom->txpid5gl[0], 0); ++ nvram_read_u8(prefix, NULL, "txpid5gla1", &sprom->txpid5gl[1], 0); ++ nvram_read_u8(prefix, NULL, "txpid5gla2", &sprom->txpid5gl[2], 0); ++ nvram_read_u8(prefix, NULL, "txpid5gla3", &sprom->txpid5gl[3], 0); ++ nvram_read_u8(prefix, NULL, "txpid5gha0", &sprom->txpid5gh[0], 0); ++ nvram_read_u8(prefix, NULL, "txpid5gha1", &sprom->txpid5gh[1], 0); ++ nvram_read_u8(prefix, NULL, "txpid5gha2", &sprom->txpid5gh[2], 0); ++ nvram_read_u8(prefix, NULL, "txpid5gha3", &sprom->txpid5gh[3], 0); ++} ++ ++static void bcm47xx_fill_sprom_r89(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u8(prefix, NULL, "tssipos2g", &sprom->fem.ghz2.tssipos, 0); ++ nvram_read_u8(prefix, NULL, "extpagain2g", ++ &sprom->fem.ghz2.extpa_gain, 0); ++ nvram_read_u8(prefix, NULL, "pdetrange2g", ++ &sprom->fem.ghz2.pdet_range, 0); ++ nvram_read_u8(prefix, NULL, "triso2g", &sprom->fem.ghz2.tr_iso, 0); ++ nvram_read_u8(prefix, NULL, "antswctl2g", &sprom->fem.ghz2.antswlut, 0); ++ nvram_read_u8(prefix, NULL, "tssipos5g", &sprom->fem.ghz5.tssipos, 0); ++ nvram_read_u8(prefix, NULL, "extpagain5g", ++ &sprom->fem.ghz5.extpa_gain, 0); ++ nvram_read_u8(prefix, NULL, "pdetrange5g", ++ &sprom->fem.ghz5.pdet_range, 0); ++ nvram_read_u8(prefix, NULL, "triso5g", &sprom->fem.ghz5.tr_iso, 0); ++ nvram_read_u8(prefix, NULL, "antswctl5g", &sprom->fem.ghz5.antswlut, 0); ++ nvram_read_u8(prefix, NULL, "tempthresh", &sprom->tempthresh, 0); ++ nvram_read_u8(prefix, NULL, "tempoffset", &sprom->tempoffset, 0); ++ nvram_read_u16(prefix, NULL, "rawtempsense", &sprom->rawtempsense, 0); ++ nvram_read_u8(prefix, NULL, "measpower", &sprom->measpower, 0); ++ nvram_read_u8(prefix, NULL, "tempsense_slope", ++ &sprom->tempsense_slope, 0); ++ nvram_read_u8(prefix, NULL, "tempcorrx", &sprom->tempcorrx, 0); ++ nvram_read_u8(prefix, NULL, "tempsense_option", ++ &sprom->tempsense_option, 0); ++ nvram_read_u8(prefix, NULL, "freqoffset_corr", ++ &sprom->freqoffset_corr, 0); ++ nvram_read_u8(prefix, NULL, "iqcal_swp_dis", &sprom->iqcal_swp_dis, 0); ++ nvram_read_u8(prefix, NULL, "hw_iqcal_en", &sprom->hw_iqcal_en, 0); ++ nvram_read_u8(prefix, NULL, "elna2g", &sprom->elna2g, 0); ++ nvram_read_u8(prefix, NULL, "elna5g", &sprom->elna5g, 0); ++ nvram_read_u8(prefix, NULL, "phycal_tempdelta", ++ &sprom->phycal_tempdelta, 0); ++ nvram_read_u8(prefix, NULL, "temps_period", &sprom->temps_period, 0); ++ nvram_read_u8(prefix, NULL, "temps_hysteresis", ++ &sprom->temps_hysteresis, 0); ++ nvram_read_u8(prefix, NULL, "measpower1", &sprom->measpower1, 0); ++ nvram_read_u8(prefix, NULL, "measpower2", &sprom->measpower2, 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr2ga0", ++ &sprom->rxgainerr2ga[0], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr2ga1", ++ &sprom->rxgainerr2ga[1], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr2ga2", ++ &sprom->rxgainerr2ga[2], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gla0", ++ &sprom->rxgainerr5gla[0], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gla1", ++ &sprom->rxgainerr5gla[1], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gla2", ++ &sprom->rxgainerr5gla[2], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gma0", ++ &sprom->rxgainerr5gma[0], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gma1", ++ &sprom->rxgainerr5gma[1], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gma2", ++ &sprom->rxgainerr5gma[2], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gha0", ++ &sprom->rxgainerr5gha[0], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gha1", ++ &sprom->rxgainerr5gha[1], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gha2", ++ &sprom->rxgainerr5gha[2], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gua0", ++ &sprom->rxgainerr5gua[0], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gua1", ++ &sprom->rxgainerr5gua[1], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gua2", ++ &sprom->rxgainerr5gua[2], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl2ga0", &sprom->noiselvl2ga[0], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl2ga1", &sprom->noiselvl2ga[1], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl2ga2", &sprom->noiselvl2ga[2], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gla0", ++ &sprom->noiselvl5gla[0], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gla1", ++ &sprom->noiselvl5gla[1], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gla2", ++ &sprom->noiselvl5gla[2], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gma0", ++ &sprom->noiselvl5gma[0], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gma1", ++ &sprom->noiselvl5gma[1], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gma2", ++ &sprom->noiselvl5gma[2], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gha0", ++ &sprom->noiselvl5gha[0], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gha1", ++ &sprom->noiselvl5gha[1], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gha2", ++ &sprom->noiselvl5gha[2], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gua0", ++ &sprom->noiselvl5gua[0], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gua1", ++ &sprom->noiselvl5gua[1], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gua2", ++ &sprom->noiselvl5gua[2], 0); ++ nvram_read_u8(prefix, NULL, "pcieingress_war", ++ &sprom->pcieingress_war, 0); ++} ++ ++static void bcm47xx_fill_sprom_r9(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u16(prefix, NULL, "cckbw202gpo", &sprom->cckbw202gpo, 0); ++ nvram_read_u16(prefix, NULL, "cckbw20ul2gpo", &sprom->cckbw20ul2gpo, 0); ++ nvram_read_u32(prefix, NULL, "legofdmbw202gpo", ++ &sprom->legofdmbw202gpo, 0); ++ nvram_read_u32(prefix, NULL, "legofdmbw20ul2gpo", ++ &sprom->legofdmbw20ul2gpo, 0); ++ nvram_read_u32(prefix, NULL, "legofdmbw205glpo", ++ &sprom->legofdmbw205glpo, 0); ++ nvram_read_u32(prefix, NULL, "legofdmbw20ul5glpo", ++ &sprom->legofdmbw20ul5glpo, 0); ++ nvram_read_u32(prefix, NULL, "legofdmbw205gmpo", ++ &sprom->legofdmbw205gmpo, 0); ++ nvram_read_u32(prefix, NULL, "legofdmbw20ul5gmpo", ++ &sprom->legofdmbw20ul5gmpo, 0); ++ nvram_read_u32(prefix, NULL, "legofdmbw205ghpo", ++ &sprom->legofdmbw205ghpo, 0); ++ nvram_read_u32(prefix, NULL, "legofdmbw20ul5ghpo", ++ &sprom->legofdmbw20ul5ghpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw202gpo", &sprom->mcsbw202gpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw20ul2gpo", &sprom->mcsbw20ul2gpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw402gpo", &sprom->mcsbw402gpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw205glpo", &sprom->mcsbw205glpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw20ul5glpo", ++ &sprom->mcsbw20ul5glpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw405glpo", &sprom->mcsbw405glpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw205gmpo", &sprom->mcsbw205gmpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw20ul5gmpo", ++ &sprom->mcsbw20ul5gmpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw405gmpo", &sprom->mcsbw405gmpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw205ghpo", &sprom->mcsbw205ghpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw20ul5ghpo", ++ &sprom->mcsbw20ul5ghpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw405ghpo", &sprom->mcsbw405ghpo, 0); ++ nvram_read_u16(prefix, NULL, "mcs32po", &sprom->mcs32po, 0); ++ nvram_read_u16(prefix, NULL, "legofdm40duppo", ++ &sprom->legofdm40duppo, 0); ++ nvram_read_u8(prefix, NULL, "sar2g", &sprom->sar2g, 0); ++ nvram_read_u8(prefix, NULL, "sar5g", &sprom->sar5g, 0); ++} ++ ++static void bcm47xx_fill_sprom_path_r4589(struct ssb_sprom *sprom, ++ const char *prefix) ++{ ++ char postfix[2]; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(sprom->core_pwr_info); i++) { ++ struct ssb_sprom_core_pwr_info *pwr_info = &sprom->core_pwr_info[i]; ++ snprintf(postfix, sizeof(postfix), "%i", i); ++ nvram_read_u8(prefix, postfix, "maxp2ga", ++ &pwr_info->maxpwr_2g, 0); ++ nvram_read_u8(prefix, postfix, "itt2ga", ++ &pwr_info->itssi_2g, 0); ++ nvram_read_u8(prefix, postfix, "itt5ga", ++ &pwr_info->itssi_5g, 0); ++ nvram_read_u16(prefix, postfix, "pa2gw0a", ++ &pwr_info->pa_2g[0], 0); ++ nvram_read_u16(prefix, postfix, "pa2gw1a", ++ &pwr_info->pa_2g[1], 0); ++ nvram_read_u16(prefix, postfix, "pa2gw2a", ++ &pwr_info->pa_2g[2], 0); ++ nvram_read_u8(prefix, postfix, "maxp5ga", ++ &pwr_info->maxpwr_5g, 0); ++ nvram_read_u8(prefix, postfix, "maxp5gha", ++ &pwr_info->maxpwr_5gh, 0); ++ nvram_read_u8(prefix, postfix, "maxp5gla", ++ &pwr_info->maxpwr_5gl, 0); ++ nvram_read_u16(prefix, postfix, "pa5gw0a", ++ &pwr_info->pa_5g[0], 0); ++ nvram_read_u16(prefix, postfix, "pa5gw1a", ++ &pwr_info->pa_5g[1], 0); ++ nvram_read_u16(prefix, postfix, "pa5gw2a", ++ &pwr_info->pa_5g[2], 0); ++ nvram_read_u16(prefix, postfix, "pa5glw0a", ++ &pwr_info->pa_5gl[0], 0); ++ nvram_read_u16(prefix, postfix, "pa5glw1a", ++ &pwr_info->pa_5gl[1], 0); ++ nvram_read_u16(prefix, postfix, "pa5glw2a", ++ &pwr_info->pa_5gl[2], 0); ++ nvram_read_u16(prefix, postfix, "pa5ghw0a", ++ &pwr_info->pa_5gh[0], 0); ++ nvram_read_u16(prefix, postfix, "pa5ghw1a", ++ &pwr_info->pa_5gh[1], 0); ++ nvram_read_u16(prefix, postfix, "pa5ghw2a", ++ &pwr_info->pa_5gh[2], 0); ++ } ++} ++ ++static void bcm47xx_fill_sprom_path_r45(struct ssb_sprom *sprom, ++ const char *prefix) ++{ ++ char postfix[2]; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(sprom->core_pwr_info); i++) { ++ struct ssb_sprom_core_pwr_info *pwr_info = &sprom->core_pwr_info[i]; ++ snprintf(postfix, sizeof(postfix), "%i", i); ++ nvram_read_u16(prefix, postfix, "pa2gw3a", ++ &pwr_info->pa_2g[3], 0); ++ nvram_read_u16(prefix, postfix, "pa5gw3a", ++ &pwr_info->pa_5g[3], 0); ++ nvram_read_u16(prefix, postfix, "pa5glw3a", ++ &pwr_info->pa_5gl[3], 0); ++ nvram_read_u16(prefix, postfix, "pa5ghw3a", ++ &pwr_info->pa_5gh[3], 0); ++ } ++} ++ ++void bcm47xx_fill_sprom_ethernet(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_macaddr(prefix, "et0macaddr", &sprom->et0mac); ++ nvram_read_u8(prefix, NULL, "et0mdcport", &sprom->et0mdcport, 0); ++ nvram_read_u8(prefix, NULL, "et0phyaddr", &sprom->et0phyaddr, 0); ++ ++ nvram_read_macaddr(prefix, "et1macaddr", &sprom->et1mac); ++ nvram_read_u8(prefix, NULL, "et1mdcport", &sprom->et1mdcport, 0); ++ nvram_read_u8(prefix, NULL, "et1phyaddr", &sprom->et1phyaddr, 0); ++ ++ nvram_read_macaddr(prefix, "macaddr", &sprom->il0mac); ++ nvram_read_macaddr(prefix, "il0macaddr", &sprom->il0mac); ++} ++ ++void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix) ++{ ++ memset(sprom, 0, sizeof(struct ssb_sprom)); ++ ++ bcm47xx_fill_sprom_ethernet(sprom, prefix); ++ ++ nvram_read_u8(prefix, NULL, "sromrev", &sprom->revision, 0); ++ ++ switch (sprom->revision) { ++ case 1: ++ bcm47xx_fill_sprom_r1234589(sprom, prefix); ++ bcm47xx_fill_sprom_r12389(sprom, prefix); ++ bcm47xx_fill_sprom_r1(sprom, prefix); ++ break; ++ case 2: ++ bcm47xx_fill_sprom_r1234589(sprom, prefix); ++ bcm47xx_fill_sprom_r12389(sprom, prefix); ++ bcm47xx_fill_sprom_r2389(sprom, prefix); ++ bcm47xx_fill_sprom_r2(sprom, prefix); ++ break; ++ case 3: ++ bcm47xx_fill_sprom_r1234589(sprom, prefix); ++ bcm47xx_fill_sprom_r12389(sprom, prefix); ++ bcm47xx_fill_sprom_r2389(sprom, prefix); ++ bcm47xx_fill_sprom_r389(sprom, prefix); ++ bcm47xx_fill_sprom_r3(sprom, prefix); ++ break; ++ case 4: ++ case 5: ++ bcm47xx_fill_sprom_r1234589(sprom, prefix); ++ bcm47xx_fill_sprom_r4589(sprom, prefix); ++ bcm47xx_fill_sprom_r458(sprom, prefix); ++ bcm47xx_fill_sprom_r45(sprom, prefix); ++ bcm47xx_fill_sprom_path_r4589(sprom, prefix); ++ bcm47xx_fill_sprom_path_r45(sprom, prefix); ++ break; ++ case 8: ++ bcm47xx_fill_sprom_r1234589(sprom, prefix); ++ bcm47xx_fill_sprom_r12389(sprom, prefix); ++ bcm47xx_fill_sprom_r2389(sprom, prefix); ++ bcm47xx_fill_sprom_r389(sprom, prefix); ++ bcm47xx_fill_sprom_r4589(sprom, prefix); ++ bcm47xx_fill_sprom_r458(sprom, prefix); ++ bcm47xx_fill_sprom_r89(sprom, prefix); ++ bcm47xx_fill_sprom_path_r4589(sprom, prefix); ++ break; ++ case 9: ++ bcm47xx_fill_sprom_r1234589(sprom, prefix); ++ bcm47xx_fill_sprom_r12389(sprom, prefix); ++ bcm47xx_fill_sprom_r2389(sprom, prefix); ++ bcm47xx_fill_sprom_r389(sprom, prefix); ++ bcm47xx_fill_sprom_r4589(sprom, prefix); ++ bcm47xx_fill_sprom_r89(sprom, prefix); ++ bcm47xx_fill_sprom_r9(sprom, prefix); ++ bcm47xx_fill_sprom_path_r4589(sprom, prefix); ++ break; ++ default: ++ pr_warn("Unsupported SPROM revision %d detected. Will extract" ++ " v1\n", sprom->revision); ++ sprom->revision = 1; ++ bcm47xx_fill_sprom_r1234589(sprom, prefix); ++ bcm47xx_fill_sprom_r12389(sprom, prefix); ++ bcm47xx_fill_sprom_r1(sprom, prefix); ++ } ++} +--- a/arch/mips/include/asm/mach-bcm47xx/bcm47xx.h ++++ b/arch/mips/include/asm/mach-bcm47xx/bcm47xx.h +@@ -44,4 +44,7 @@ union bcm47xx_bus { + extern union bcm47xx_bus bcm47xx_bus; + extern enum bcm47xx_bus_type bcm47xx_bus_type; + ++void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix); ++void bcm47xx_fill_sprom_ethernet(struct ssb_sprom *sprom, const char *prefix); ++ + #endif /* __ASM_BCM47XX_H */ diff --git a/target/linux/brcm47xx/patches-3.3/004-MIPS-BCM47XX-provide-sprom-to-bcma-bus.patch b/target/linux/brcm47xx/patches-3.3/004-MIPS-BCM47XX-provide-sprom-to-bcma-bus.patch new file mode 100644 index 000000000..686832379 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/004-MIPS-BCM47XX-provide-sprom-to-bcma-bus.patch @@ -0,0 +1,80 @@ +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -3,7 +3,7 @@ + * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2006 Michael Buesch <m@bues.ch> + * Copyright (C) 2010 Waldemar Brodkorb <wbx@openadk.org> +- * Copyright (C) 2010-2011 Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright (C) 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the +@@ -85,7 +85,7 @@ static void bcm47xx_machine_halt(void) + } + + #ifdef CONFIG_BCM47XX_SSB +-static int bcm47xx_get_sprom(struct ssb_bus *bus, struct ssb_sprom *out) ++static int bcm47xx_get_sprom_ssb(struct ssb_bus *bus, struct ssb_sprom *out) + { + char prefix[10]; + +@@ -102,7 +102,7 @@ static int bcm47xx_get_sprom(struct ssb_ + } + + static int bcm47xx_get_invariants(struct ssb_bus *bus, +- struct ssb_init_invariants *iv) ++ struct ssb_init_invariants *iv) + { + char buf[20]; + +@@ -132,7 +132,7 @@ static void __init bcm47xx_register_ssb( + char buf[100]; + struct ssb_mipscore *mcore; + +- err = ssb_arch_register_fallback_sprom(&bcm47xx_get_sprom); ++ err = ssb_arch_register_fallback_sprom(&bcm47xx_get_sprom_ssb); + if (err) + printk(KERN_WARNING "bcm47xx: someone else already registered" + " a ssb SPROM callback handler (err %d)\n", err); +@@ -159,10 +159,41 @@ static void __init bcm47xx_register_ssb( + #endif + + #ifdef CONFIG_BCM47XX_BCMA ++static int bcm47xx_get_sprom_bcma(struct bcma_bus *bus, struct ssb_sprom *out) ++{ ++ char prefix[10]; ++ struct bcma_device *core; ++ ++ switch (bus->hosttype) { ++ case BCMA_HOSTTYPE_PCI: ++ snprintf(prefix, sizeof(prefix), "pci/%u/%u/", ++ bus->host_pci->bus->number + 1, ++ PCI_SLOT(bus->host_pci->devfn)); ++ bcm47xx_fill_sprom(out, prefix); ++ return 0; ++ case BCMA_HOSTTYPE_SOC: ++ bcm47xx_fill_sprom_ethernet(out, NULL); ++ core = bcma_find_core(bus, BCMA_CORE_80211); ++ if (core) { ++ snprintf(prefix, sizeof(prefix), "sb/%u/", ++ core->core_index); ++ bcm47xx_fill_sprom(out, prefix); ++ } ++ return 0; ++ default: ++ pr_warn("bcm47xx: unable to fill SPROM for given bustype.\n"); ++ return -EINVAL; ++ } ++} ++ + static void __init bcm47xx_register_bcma(void) + { + int err; + ++ err = bcma_arch_register_fallback_sprom(&bcm47xx_get_sprom_bcma); ++ if (err) ++ pr_warn("bcm47xx: someone else already registered a bcma SPROM callback handler (err %d)\n", err); ++ + err = bcma_host_soc_register(&bcm47xx_bus.bcma); + if (err) + panic("Failed to initialize BCMA bus (err %d)", err); diff --git a/target/linux/brcm47xx/patches-3.3/005-ssb-remove-rev-from-boardinfo.patch b/target/linux/brcm47xx/patches-3.3/005-ssb-remove-rev-from-boardinfo.patch new file mode 100644 index 000000000..abfaafb47 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/005-ssb-remove-rev-from-boardinfo.patch @@ -0,0 +1,11 @@ +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -115,8 +115,6 @@ static int bcm47xx_get_invariants(struct + iv->boardinfo.vendor = SSB_BOARDVENDOR_BCM; + if (nvram_getenv("boardtype", buf, sizeof(buf)) >= 0) + iv->boardinfo.type = (u16)simple_strtoul(buf, NULL, 0); +- if (nvram_getenv("boardrev", buf, sizeof(buf)) >= 0) +- iv->boardinfo.rev = (u16)simple_strtoul(buf, NULL, 0); + + bcm47xx_fill_sprom(&iv->sprom, NULL); + diff --git a/target/linux/brcm47xx/patches-3.3/006-MIPS-bcm47xx-refactor-fetching-board-data.patch b/target/linux/brcm47xx/patches-3.3/006-MIPS-bcm47xx-refactor-fetching-board-data.patch new file mode 100644 index 000000000..f114501a4 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/006-MIPS-bcm47xx-refactor-fetching-board-data.patch @@ -0,0 +1,46 @@ +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -109,12 +109,7 @@ static int bcm47xx_get_invariants(struct + /* Fill boardinfo structure */ + memset(&(iv->boardinfo), 0 , sizeof(struct ssb_boardinfo)); + +- if (nvram_getenv("boardvendor", buf, sizeof(buf)) >= 0) +- iv->boardinfo.vendor = (u16)simple_strtoul(buf, NULL, 0); +- else +- iv->boardinfo.vendor = SSB_BOARDVENDOR_BCM; +- if (nvram_getenv("boardtype", buf, sizeof(buf)) >= 0) +- iv->boardinfo.type = (u16)simple_strtoul(buf, NULL, 0); ++ bcm47xx_fill_ssb_boardinfo(&iv->boardinfo, NULL); + + bcm47xx_fill_sprom(&iv->sprom, NULL); + +--- a/arch/mips/bcm47xx/sprom.c ++++ b/arch/mips/bcm47xx/sprom.c +@@ -618,3 +618,15 @@ void bcm47xx_fill_sprom(struct ssb_sprom + bcm47xx_fill_sprom_r1(sprom, prefix); + } + } ++ ++#ifdef CONFIG_BCM47XX_SSB ++void bcm47xx_fill_ssb_boardinfo(struct ssb_boardinfo *boardinfo, ++ const char *prefix) ++{ ++ nvram_read_u16(prefix, NULL, "boardvendor", &boardinfo->vendor, 0); ++ if (!boardinfo->vendor) ++ boardinfo->vendor = SSB_BOARDVENDOR_BCM; ++ ++ nvram_read_u16(prefix, NULL, "boardtype", &boardinfo->type, 0); ++} ++#endif +--- a/arch/mips/include/asm/mach-bcm47xx/bcm47xx.h ++++ b/arch/mips/include/asm/mach-bcm47xx/bcm47xx.h +@@ -47,4 +47,9 @@ extern enum bcm47xx_bus_type bcm47xx_bus + void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix); + void bcm47xx_fill_sprom_ethernet(struct ssb_sprom *sprom, const char *prefix); + ++#ifdef CONFIG_BCM47XX_SSB ++void bcm47xx_fill_ssb_boardinfo(struct ssb_boardinfo *boardinfo, ++ const char *prefix); ++#endif ++ + #endif /* __ASM_BCM47XX_H */ diff --git a/target/linux/brcm47xx/patches-3.3/007-bcma-add-boardinfo-struct.patch b/target/linux/brcm47xx/patches-3.3/007-bcma-add-boardinfo-struct.patch new file mode 100644 index 000000000..b3514c682 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/007-bcma-add-boardinfo-struct.patch @@ -0,0 +1,41 @@ +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -190,6 +190,8 @@ static void __init bcm47xx_register_bcma + err = bcma_host_soc_register(&bcm47xx_bus.bcma); + if (err) + panic("Failed to initialize BCMA bus (err %d)", err); ++ ++ bcm47xx_fill_bcma_boardinfo(&bcm47xx_bus.bcma.bus.boardinfo, NULL); + } + #endif + +--- a/arch/mips/bcm47xx/sprom.c ++++ b/arch/mips/bcm47xx/sprom.c +@@ -630,3 +630,15 @@ void bcm47xx_fill_ssb_boardinfo(struct s + nvram_read_u16(prefix, NULL, "boardtype", &boardinfo->type, 0); + } + #endif ++ ++#ifdef CONFIG_BCM47XX_BCMA ++void bcm47xx_fill_bcma_boardinfo(struct bcma_boardinfo *boardinfo, ++ const char *prefix) ++{ ++ nvram_read_u16(prefix, NULL, "boardvendor", &boardinfo->vendor, 0); ++ if (!boardinfo->vendor) ++ boardinfo->vendor = SSB_BOARDVENDOR_BCM; ++ ++ nvram_read_u16(prefix, NULL, "boardtype", &boardinfo->type, 0); ++} ++#endif +--- a/arch/mips/include/asm/mach-bcm47xx/bcm47xx.h ++++ b/arch/mips/include/asm/mach-bcm47xx/bcm47xx.h +@@ -51,5 +51,9 @@ void bcm47xx_fill_sprom_ethernet(struct + void bcm47xx_fill_ssb_boardinfo(struct ssb_boardinfo *boardinfo, + const char *prefix); + #endif ++#ifdef CONFIG_BCM47XX_BCMA ++void bcm47xx_fill_bcma_boardinfo(struct bcma_boardinfo *boardinfo, ++ const char *prefix); ++#endif + + #endif /* __ASM_BCM47XX_H */ diff --git a/target/linux/brcm47xx/patches-3.3/008-MIPS-bcm47xx-read-baordrev-without-prefix-from-sprom.patch b/target/linux/brcm47xx/patches-3.3/008-MIPS-bcm47xx-read-baordrev-without-prefix-from-sprom.patch new file mode 100644 index 000000000..108987d83 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/008-MIPS-bcm47xx-read-baordrev-without-prefix-from-sprom.patch @@ -0,0 +1,11 @@ +--- a/arch/mips/bcm47xx/sprom.c ++++ b/arch/mips/bcm47xx/sprom.c +@@ -165,6 +165,8 @@ static void bcm47xx_fill_sprom_r1234589( + const char *prefix) + { + nvram_read_u16(prefix, NULL, "boardrev", &sprom->board_rev, 0); ++ if (!sprom->board_rev) ++ nvram_read_u16(NULL, NULL, "boardrev", &sprom->board_rev, 0); + nvram_read_u16(prefix, NULL, "boardnum", &sprom->board_num, 0); + nvram_read_u8(prefix, NULL, "ledbh0", &sprom->gpio0, 0xff); + nvram_read_u8(prefix, NULL, "ledbh1", &sprom->gpio1, 0xff); diff --git a/target/linux/brcm47xx/patches-3.3/009-bcma_reorder_sprom_fill.patch b/target/linux/brcm47xx/patches-3.3/009-bcma_reorder_sprom_fill.patch new file mode 100644 index 000000000..3778e64b8 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/009-bcma_reorder_sprom_fill.patch @@ -0,0 +1,44 @@ +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -90,6 +90,7 @@ static int bcm47xx_get_sprom_ssb(struct + char prefix[10]; + + if (bus->bustype == SSB_BUSTYPE_PCI) { ++ memset(out, 0, sizeof(struct ssb_sprom)); + snprintf(prefix, sizeof(prefix), "pci/%u/%u/", + bus->host_pci->bus->number + 1, + PCI_SLOT(bus->host_pci->devfn)); +@@ -111,6 +112,7 @@ static int bcm47xx_get_invariants(struct + + bcm47xx_fill_ssb_boardinfo(&iv->boardinfo, NULL); + ++ memset(&iv->sprom, 0, sizeof(struct ssb_sprom)); + bcm47xx_fill_sprom(&iv->sprom, NULL); + + if (nvram_getenv("cardbus", buf, sizeof(buf)) >= 0) +@@ -159,12 +161,14 @@ static int bcm47xx_get_sprom_bcma(struct + + switch (bus->hosttype) { + case BCMA_HOSTTYPE_PCI: ++ memset(out, 0, sizeof(struct ssb_sprom)); + snprintf(prefix, sizeof(prefix), "pci/%u/%u/", + bus->host_pci->bus->number + 1, + PCI_SLOT(bus->host_pci->devfn)); + bcm47xx_fill_sprom(out, prefix); + return 0; + case BCMA_HOSTTYPE_SOC: ++ memset(out, 0, sizeof(struct ssb_sprom)); + bcm47xx_fill_sprom_ethernet(out, NULL); + core = bcma_find_core(bus, BCMA_CORE_80211); + if (core) { +--- a/arch/mips/bcm47xx/sprom.c ++++ b/arch/mips/bcm47xx/sprom.c +@@ -557,8 +557,6 @@ void bcm47xx_fill_sprom_ethernet(struct + + void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix) + { +- memset(sprom, 0, sizeof(struct ssb_sprom)); +- + bcm47xx_fill_sprom_ethernet(sprom, prefix); + + nvram_read_u8(prefix, NULL, "sromrev", &sprom->revision, 0); diff --git a/target/linux/brcm47xx/patches-3.3/010-MIPS-BCM47xx-Fix-BCMA_DRIVER_PCI_HOSTMODE.patch b/target/linux/brcm47xx/patches-3.3/010-MIPS-BCM47xx-Fix-BCMA_DRIVER_PCI_HOSTMODE.patch new file mode 100644 index 000000000..9a94eb08b --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/010-MIPS-BCM47xx-Fix-BCMA_DRIVER_PCI_HOSTMODE.patch @@ -0,0 +1,10 @@ +--- a/arch/mips/bcm47xx/Kconfig ++++ b/arch/mips/bcm47xx/Kconfig +@@ -21,6 +21,7 @@ config BCM47XX_BCMA + select BCMA + select BCMA_HOST_SOC + select BCMA_DRIVER_MIPS ++ select BCMA_HOST_PCI if PCI + select BCMA_DRIVER_PCI_HOSTMODE if PCI + default y + help diff --git a/target/linux/brcm47xx/patches-3.3/020-bcma-move-parallel-flash-into-a-union.patch b/target/linux/brcm47xx/patches-3.3/020-bcma-move-parallel-flash-into-a-union.patch new file mode 100644 index 000000000..98c855fbd --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/020-bcma-move-parallel-flash-into-a-union.patch @@ -0,0 +1,129 @@ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -50,6 +50,9 @@ static void early_nvram_init(void) + #ifdef CONFIG_BCM47XX_BCMA + case BCM47XX_BUS_TYPE_BCMA: + bcma_cc = &bcm47xx_bus.bcma.bus.drv_cc; ++ if (bcma_cc->flash_type != BCMA_PFLASH) ++ return; ++ + base = bcma_cc->pflash.window; + lim = bcma_cc->pflash.window_size; + break; +--- a/drivers/bcma/driver_mips.c ++++ b/drivers/bcma/driver_mips.c +@@ -189,6 +189,7 @@ static void bcma_core_mips_flash_detect( + break; + case BCMA_CC_FLASHT_PARA: + bcma_info(bus, "found parallel flash.\n"); ++ bus->drv_cc.flash_type = BCMA_PFLASH; + bus->drv_cc.pflash.window = 0x1c000000; + bus->drv_cc.pflash.window_size = 0x02000000; + +--- a/include/linux/bcma/bcma_driver_chipcommon.h ++++ b/include/linux/bcma/bcma_driver_chipcommon.h +@@ -122,10 +122,68 @@ + #define BCMA_CC_JCTL_EXT_EN 2 /* Enable external targets */ + #define BCMA_CC_JCTL_EN 1 /* Enable Jtag master */ + #define BCMA_CC_FLASHCTL 0x0040 ++ ++/* Start/busy bit in flashcontrol */ ++#define BCMA_CC_FLASHCTL_OPCODE 0x000000ff ++#define BCMA_CC_FLASHCTL_ACTION 0x00000700 ++#define BCMA_CC_FLASHCTL_CS_ACTIVE 0x00001000 /* Chip Select Active, rev >= 20 */ + #define BCMA_CC_FLASHCTL_START 0x80000000 + #define BCMA_CC_FLASHCTL_BUSY BCMA_CC_FLASHCTL_START ++ ++/* flashcontrol action+opcodes for ST flashes */ ++#define BCMA_CC_FLASHCTL_ST_WREN 0x0006 /* Write Enable */ ++#define BCMA_CC_FLASHCTL_ST_WRDIS 0x0004 /* Write Disable */ ++#define BCMA_CC_FLASHCTL_ST_RDSR 0x0105 /* Read Status Register */ ++#define BCMA_CC_FLASHCTL_ST_WRSR 0x0101 /* Write Status Register */ ++#define BCMA_CC_FLASHCTL_ST_READ 0x0303 /* Read Data Bytes */ ++#define BCMA_CC_FLASHCTL_ST_PP 0x0302 /* Page Program */ ++#define BCMA_CC_FLASHCTL_ST_SE 0x02d8 /* Sector Erase */ ++#define BCMA_CC_FLASHCTL_ST_BE 0x00c7 /* Bulk Erase */ ++#define BCMA_CC_FLASHCTL_ST_DP 0x00b9 /* Deep Power-down */ ++#define BCMA_CC_FLASHCTL_ST_RES 0x03ab /* Read Electronic Signature */ ++#define BCMA_CC_FLASHCTL_ST_CSA 0x1000 /* Keep chip select asserted */ ++#define BCMA_CC_FLASHCTL_ST_SSE 0x0220 /* Sub-sector Erase */ ++ ++ ++/* flashcontrol action+opcodes for Atmel flashes */ ++#define BCMA_CC_FLASHCTL_AT_READ 0x07e8 ++#define BCMA_CC_FLASHCTL_AT_PAGE_READ 0x07d2 ++#define BCMA_CC_FLASHCTL_AT_BUF1_READ ++#define BCMA_CC_FLASHCTL_AT_BUF2_READ ++#define BCMA_CC_FLASHCTL_AT_STATUS 0x01d7 ++#define BCMA_CC_FLASHCTL_AT_BUF1_WRITE 0x0384 ++#define BCMA_CC_FLASHCTL_AT_BUF2_WRITE 0x0387 ++#define BCMA_CC_FLASHCTL_AT_BUF1_ERASE_PROGRAM 0x0283 ++#define BCMA_CC_FLASHCTL_AT_BUF2_ERASE_PROGRAM 0x0286 ++#define BCMA_CC_FLASHCTL_AT_BUF1_PROGRAM 0x0288 ++#define BCMA_CC_FLASHCTL_AT_BUF2_PROGRAM 0x0289 ++#define BCMA_CC_FLASHCTL_AT_PAGE_ERASE 0x0281 ++#define BCMA_CC_FLASHCTL_AT_BLOCK_ERASE 0x0250 ++#define BCMA_CC_FLASHCTL_AT_BUF1_WRITE_ERASE_PROGRAM 0x0382 ++#define BCMA_CC_FLASHCTL_AT_BUF2_WRITE_ERASE_PROGRAM 0x0385 ++#define BCMA_CC_FLASHCTL_AT_BUF1_LOAD 0x0253 ++#define BCMA_CC_FLASHCTL_AT_BUF2_LOAD 0x0255 ++#define BCMA_CC_FLASHCTL_AT_BUF1_COMPARE 0x0260 ++#define BCMA_CC_FLASHCTL_AT_BUF2_COMPARE 0x0261 ++#define BCMA_CC_FLASHCTL_AT_BUF1_REPROGRAM 0x0258 ++#define BCMA_CC_FLASHCTL_AT_BUF2_REPROGRAM 0x0259 ++ + #define BCMA_CC_FLASHADDR 0x0044 + #define BCMA_CC_FLASHDATA 0x0048 ++ ++/* Status register bits for ST flashes */ ++#define BCMA_CC_FLASHDATA_ST_WIP 0x01 /* Write In Progress */ ++#define BCMA_CC_FLASHDATA_ST_WEL 0x02 /* Write Enable Latch */ ++#define BCMA_CC_FLASHDATA_ST_BP_MASK 0x1c /* Block Protect */ ++#define BCMA_CC_FLASHDATA_ST_BP_SHIFT 2 ++#define BCMA_CC_FLASHDATA_ST_SRWD 0x80 /* Status Register Write Disable */ ++ ++/* Status register bits for Atmel flashes */ ++#define BCMA_CC_FLASHDATA_AT_READY 0x80 ++#define BCMA_CC_FLASHDATA_AT_MISMATCH 0x40 ++#define BCMA_CC_FLASHDATA_AT_ID_MASK 0x38 ++#define BCMA_CC_FLASHDATA_AT_ID_SHIFT 3 ++ + #define BCMA_CC_BCAST_ADDR 0x0050 + #define BCMA_CC_BCAST_DATA 0x0054 + #define BCMA_CC_GPIOPULLUP 0x0058 /* Rev >= 20 only */ +@@ -360,6 +418,12 @@ + /* 4313 Chip specific ChipControl register bits */ + #define BCMA_CCTRL_4313_12MA_LED_DRIVE 0x00000007 /* 12 mA drive strengh for later 4313 */ + ++#define BCMA_FLASH2 0x1c000000 /* Flash Region 2 (region 1 shadowed here) */ ++#define BCMA_FLASH2_SZ 0x02000000 /* Size of Flash Region 2 */ ++#define BCMA_FLASH1 0x1fc00000 /* MIPS Flash Region 1 */ ++#define BCMA_FLASH1_SZ 0x00400000 /* MIPS Size of Flash Region 1 */ ++ ++ + /* Data for the PMU, if available. + * Check availability with ((struct bcma_chipcommon)->capabilities & BCMA_CC_CAP_PMU) + */ +@@ -369,6 +433,10 @@ struct bcma_chipcommon_pmu { + }; + + #ifdef CONFIG_BCMA_DRIVER_MIPS ++enum bcma_flash_type { ++ BCMA_PFLASH, ++}; ++ + struct bcma_pflash { + u8 buswidth; + u32 window; +@@ -394,7 +462,10 @@ struct bcma_drv_cc { + u16 fast_pwrup_delay; + struct bcma_chipcommon_pmu pmu; + #ifdef CONFIG_BCMA_DRIVER_MIPS +- struct bcma_pflash pflash; ++ enum bcma_flash_type flash_type; ++ union { ++ struct bcma_pflash pflash; ++ }; + + int nr_serial_ports; + struct bcma_serial_port serial_ports[4]; diff --git a/target/linux/brcm47xx/patches-3.3/021-bcma-add-serial-flash-support-to-bcma.patch b/target/linux/brcm47xx/patches-3.3/021-bcma-add-serial-flash-support-to-bcma.patch new file mode 100644 index 000000000..7382636b3 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/021-bcma-add-serial-flash-support-to-bcma.patch @@ -0,0 +1,505 @@ +--- a/drivers/bcma/Kconfig ++++ b/drivers/bcma/Kconfig +@@ -38,6 +38,11 @@ config BCMA_HOST_SOC + bool + depends on BCMA_DRIVER_MIPS + ++config BCMA_SFLASH ++ bool ++ depends on BCMA_DRIVER_MIPS ++ default y ++ + config BCMA_DRIVER_MIPS + bool "BCMA Broadcom MIPS core driver" + depends on BCMA && MIPS +--- a/drivers/bcma/Makefile ++++ b/drivers/bcma/Makefile +@@ -1,5 +1,6 @@ + bcma-y += main.o scan.o core.o sprom.o + bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o ++bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o + bcma-y += driver_pci.o + bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o + bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o +--- a/drivers/bcma/bcma_private.h ++++ b/drivers/bcma/bcma_private.h +@@ -51,6 +51,11 @@ void bcma_chipco_serial_init(struct bcma + u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc); + u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc); + ++#ifdef CONFIG_BCMA_SFLASH ++/* driver_chipcommon_sflash.c */ ++int bcma_sflash_init(struct bcma_drv_cc *cc); ++#endif /* CONFIG_BCMA_SFLASH */ ++ + #ifdef CONFIG_BCMA_HOST_PCI + /* host_pci.c */ + extern int __init bcma_host_pci_init(void); +--- /dev/null ++++ b/drivers/bcma/driver_chipcommon_sflash.c +@@ -0,0 +1,398 @@ ++/* ++ * Broadcom SiliconBackplane chipcommon serial flash interface ++ * ++ * Copyright 2011, Jonas Gorski <jonas.gorski@gmail.com> ++ * Copyright 2011, 2012, Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright 2010, Broadcom Corporation ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++ ++#include <linux/bcma/bcma.h> ++#include <linux/bcma/bcma_driver_chipcommon.h> ++#include <linux/delay.h> ++ ++#include "bcma_private.h" ++ ++#define NUM_RETRIES 3 ++ ++ ++/* Issue a serial flash command */ ++static inline void bcma_sflash_cmd(struct bcma_drv_cc *cc, u32 opcode) ++{ ++ bcma_cc_write32(cc, BCMA_CC_FLASHCTL, ++ BCMA_CC_FLASHCTL_START | opcode); ++ while (bcma_cc_read32(cc, BCMA_CC_FLASHCTL) & BCMA_CC_FLASHCTL_BUSY) ++ ; ++} ++ ++ ++static inline void bcma_sflash_write_u8(struct bcma_drv_cc *cc, ++ u32 offset, u8 byte) ++{ ++ bcma_cc_write32(cc, BCMA_CC_FLASHADDR, offset); ++ bcma_cc_write32(cc, BCMA_CC_FLASHDATA, byte); ++} ++ ++/* Initialize serial flash access */ ++int bcma_sflash_init(struct bcma_drv_cc *cc) ++{ ++ u32 id, id2; ++ ++ memset(&cc->sflash, 0, sizeof(struct bcma_sflash)); ++ ++ switch (cc->capabilities & BCMA_CC_CAP_FLASHT) { ++ case BCMA_CC_FLASHT_STSER: ++ /* Probe for ST chips */ ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_DP); ++ bcma_cc_write32(cc, BCMA_CC_FLASHADDR, 0); ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_RES); ++ id = bcma_cc_read32(cc, BCMA_CC_FLASHDATA); ++ cc->sflash.blocksize = 64 * 1024; ++ switch (id) { ++ case 0x11: ++ /* ST M25P20 2 Mbit Serial Flash */ ++ cc->sflash.numblocks = 4; ++ break; ++ case 0x12: ++ /* ST M25P40 4 Mbit Serial Flash */ ++ cc->sflash.numblocks = 8; ++ break; ++ case 0x13: ++ /* ST M25P80 8 Mbit Serial Flash */ ++ cc->sflash.numblocks = 16; ++ break; ++ case 0x14: ++ /* ST M25P16 16 Mbit Serial Flash */ ++ cc->sflash.numblocks = 32; ++ break; ++ case 0x15: ++ /* ST M25P32 32 Mbit Serial Flash */ ++ cc->sflash.numblocks = 64; ++ break; ++ case 0x16: ++ /* ST M25P64 64 Mbit Serial Flash */ ++ cc->sflash.numblocks = 128; ++ break; ++ case 0x17: ++ /* ST M25FL128 128 Mbit Serial Flash */ ++ cc->sflash.numblocks = 256; ++ break; ++ case 0xbf: ++ /* All of the following flashes are SST with ++ * 4KB subsectors. Others should be added but ++ * We'll have to revamp the way we identify them ++ * since RES is not eough to disambiguate them. ++ */ ++ cc->sflash.blocksize = 4 * 1024; ++ bcma_cc_write32(cc, BCMA_CC_FLASHADDR, 1); ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_RES); ++ id2 = bcma_cc_read32(cc, BCMA_CC_FLASHDATA); ++ switch (id2) { ++ case 1: ++ /* SST25WF512 512 Kbit Serial Flash */ ++ case 0x48: ++ /* SST25VF512 512 Kbit Serial Flash */ ++ cc->sflash.numblocks = 16; ++ break; ++ case 2: ++ /* SST25WF010 1 Mbit Serial Flash */ ++ case 0x49: ++ /* SST25VF010 1 Mbit Serial Flash */ ++ cc->sflash.numblocks = 32; ++ break; ++ case 3: ++ /* SST25WF020 2 Mbit Serial Flash */ ++ case 0x43: ++ /* SST25VF020 2 Mbit Serial Flash */ ++ cc->sflash.numblocks = 64; ++ break; ++ case 4: ++ /* SST25WF040 4 Mbit Serial Flash */ ++ case 0x44: ++ /* SST25VF040 4 Mbit Serial Flash */ ++ case 0x8d: ++ /* SST25VF040B 4 Mbit Serial Flash */ ++ cc->sflash.numblocks = 128; ++ break; ++ case 5: ++ /* SST25WF080 8 Mbit Serial Flash */ ++ case 0x8e: ++ /* SST25VF080B 8 Mbit Serial Flash */ ++ cc->sflash.numblocks = 256; ++ break; ++ case 0x41: ++ /* SST25VF016 16 Mbit Serial Flash */ ++ cc->sflash.numblocks = 512; ++ break; ++ case 0x4a: ++ /* SST25VF032 32 Mbit Serial Flash */ ++ cc->sflash.numblocks = 1024; ++ break; ++ case 0x4b: ++ /* SST25VF064 64 Mbit Serial Flash */ ++ cc->sflash.numblocks = 2048; ++ break; ++ } ++ break; ++ } ++ break; ++ ++ case BCMA_CC_FLASHT_ATSER: ++ /* Probe for Atmel chips */ ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_STATUS); ++ id = bcma_cc_read32(cc, BCMA_CC_FLASHDATA) & 0x3c; ++ switch (id) { ++ case 0xc: ++ /* Atmel AT45DB011 1Mbit Serial Flash */ ++ cc->sflash.blocksize = 256; ++ cc->sflash.numblocks = 512; ++ break; ++ case 0x14: ++ /* Atmel AT45DB021 2Mbit Serial Flash */ ++ cc->sflash.blocksize = 256; ++ cc->sflash.numblocks = 1024; ++ break; ++ case 0x1c: ++ /* Atmel AT45DB041 4Mbit Serial Flash */ ++ cc->sflash.blocksize = 256; ++ cc->sflash.numblocks = 2048; ++ break; ++ case 0x24: ++ /* Atmel AT45DB081 8Mbit Serial Flash */ ++ cc->sflash.blocksize = 256; ++ cc->sflash.numblocks = 4096; ++ break; ++ case 0x2c: ++ /* Atmel AT45DB161 16Mbit Serial Flash */ ++ cc->sflash.blocksize = 512; ++ cc->sflash.numblocks = 4096; ++ break; ++ case 0x34: ++ /* Atmel AT45DB321 32Mbit Serial Flash */ ++ cc->sflash.blocksize = 512; ++ cc->sflash.numblocks = 8192; ++ break; ++ case 0x3c: ++ /* Atmel AT45DB642 64Mbit Serial Flash */ ++ cc->sflash.blocksize = 1024; ++ cc->sflash.numblocks = 8192; ++ break; ++ } ++ break; ++ } ++ ++ cc->sflash.size = cc->sflash.blocksize * cc->sflash.numblocks; ++ ++ return cc->sflash.size ? 0 : -ENODEV; ++} ++ ++/* Read len bytes starting at offset into buf. Returns number of bytes read. */ ++int bcma_sflash_read(struct bcma_drv_cc *cc, u32 offset, u32 len, u8 *buf) ++{ ++ u8 *from, *to; ++ u32 cnt, i; ++ ++ if (!len) ++ return 0; ++ ++ if ((offset + len) > cc->sflash.size) ++ return -EINVAL; ++ ++ if ((len >= 4) && (offset & 3)) ++ cnt = 4 - (offset & 3); ++ else if ((len >= 4) && ((u32)buf & 3)) ++ cnt = 4 - ((u32)buf & 3); ++ else ++ cnt = len; ++ ++ from = (u8 *)KSEG0ADDR(BCMA_FLASH2 + offset); ++ ++ to = (u8 *)buf; ++ ++ if (cnt < 4) { ++ for (i = 0; i < cnt; i++) { ++ *to = readb(from); ++ from++; ++ to++; ++ } ++ return cnt; ++ } ++ ++ while (cnt >= 4) { ++ *(u32 *)to = readl(from); ++ from += 4; ++ to += 4; ++ cnt -= 4; ++ } ++ ++ return len - cnt; ++} ++ ++/* Poll for command completion. Returns zero when complete. */ ++int bcma_sflash_poll(struct bcma_drv_cc *cc, u32 offset) ++{ ++ if (offset >= cc->sflash.size) ++ return -22; ++ ++ switch (cc->capabilities & BCMA_CC_CAP_FLASHT) { ++ case BCMA_CC_FLASHT_STSER: ++ /* Check for ST Write In Progress bit */ ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_RDSR); ++ return bcma_cc_read32(cc, BCMA_CC_FLASHDATA) ++ & BCMA_CC_FLASHDATA_ST_WIP; ++ case BCMA_CC_FLASHT_ATSER: ++ /* Check for Atmel Ready bit */ ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_STATUS); ++ return !(bcma_cc_read32(cc, BCMA_CC_FLASHDATA) ++ & BCMA_CC_FLASHDATA_AT_READY); ++ } ++ ++ return 0; ++} ++ ++ ++static int sflash_st_write(struct bcma_drv_cc *cc, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ int written = 1; ++ ++ /* Enable writes */ ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_WREN); ++ bcma_sflash_write_u8(cc, offset, *buf++); ++ /* Issue a page program with CSA bit set */ ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_CSA | BCMA_CC_FLASHCTL_ST_PP); ++ offset++; ++ len--; ++ while (len > 0) { ++ if ((offset & 255) == 0) { ++ /* Page boundary, poll droping cs and return */ ++ bcma_cc_write32(cc, BCMA_CC_FLASHCTL, 0); ++ udelay(1); ++ if (!bcma_sflash_poll(cc, offset)) { ++ /* Flash rejected command */ ++ return -EAGAIN; ++ } ++ return written; ++ } else { ++ /* Write single byte */ ++ bcma_sflash_cmd(cc, ++ BCMA_CC_FLASHCTL_ST_CSA | ++ *buf++); ++ } ++ written++; ++ offset++; ++ len--; ++ } ++ /* All done, drop cs & poll */ ++ bcma_cc_write32(cc, BCMA_CC_FLASHCTL, 0); ++ udelay(1); ++ if (!bcma_sflash_poll(cc, offset)) { ++ /* Flash rejected command */ ++ return -EAGAIN; ++ } ++ return written; ++} ++ ++static int sflash_at_write(struct bcma_drv_cc *cc, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ struct bcma_sflash *sfl = &cc->sflash; ++ u32 page, byte, mask; ++ int ret = 0; ++ ++ mask = sfl->blocksize - 1; ++ page = (offset & ~mask) << 1; ++ byte = offset & mask; ++ /* Read main memory page into buffer 1 */ ++ if (byte || (len < sfl->blocksize)) { ++ int i = 100; ++ bcma_cc_write32(cc, BCMA_CC_FLASHADDR, page); ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_BUF1_LOAD); ++ /* 250 us for AT45DB321B */ ++ while (i > 0 && bcma_sflash_poll(cc, offset)) { ++ udelay(10); ++ i--; ++ } ++ BUG_ON(!bcma_sflash_poll(cc, offset)); ++ } ++ /* Write into buffer 1 */ ++ for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) { ++ bcma_sflash_write_u8(cc, byte++, *buf++); ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_BUF1_WRITE); ++ } ++ /* Write buffer 1 into main memory page */ ++ bcma_cc_write32(cc, BCMA_CC_FLASHADDR, page); ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_BUF1_PROGRAM); ++ ++ return ret; ++} ++ ++/* Write len bytes starting at offset into buf. Returns number of bytes ++ * written. Caller should poll for completion. ++ */ ++int bcma_sflash_write(struct bcma_drv_cc *cc, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ struct bcma_sflash *sfl; ++ int ret = 0, tries = NUM_RETRIES; ++ ++ if (!len) ++ return 0; ++ ++ if ((offset + len) > cc->sflash.size) ++ return -EINVAL; ++ ++ sfl = &cc->sflash; ++ switch (cc->capabilities & BCMA_CC_CAP_FLASHT) { ++ case BCMA_CC_FLASHT_STSER: ++ do { ++ ret = sflash_st_write(cc, offset, len, buf); ++ tries--; ++ } while (ret == -EAGAIN && tries > 0); ++ ++ if (ret == -EAGAIN && tries == 0) { ++ bcma_info(cc->core->bus, "ST Flash rejected write\n"); ++ ret = -EIO; ++ } ++ break; ++ case BCMA_CC_FLASHT_ATSER: ++ ret = sflash_at_write(cc, offset, len, buf); ++ break; ++ } ++ ++ return ret; ++} ++ ++/* Erase a region. Returns number of bytes scheduled for erasure. ++ * Caller should poll for completion. ++ */ ++int bcma_sflash_erase(struct bcma_drv_cc *cc, u32 offset) ++{ ++ struct bcma_sflash *sfl; ++ ++ if (offset >= cc->sflash.size) ++ return -EINVAL; ++ ++ sfl = &cc->sflash; ++ switch (cc->capabilities & BCMA_CC_CAP_FLASHT) { ++ case BCMA_CC_FLASHT_STSER: ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_WREN); ++ bcma_cc_write32(cc, BCMA_CC_FLASHADDR, offset); ++ /* Newer flashes have "sub-sectors" which can be erased independently ++ * with a new command: ST_SSE. The ST_SE command erases 64KB just as ++ * before. ++ */ ++ if (sfl->blocksize < (64 * 1024)) ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_SSE); ++ else ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_SE); ++ return sfl->blocksize; ++ case BCMA_CC_FLASHT_ATSER: ++ bcma_cc_write32(cc, BCMA_CC_FLASHADDR, offset << 1); ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_PAGE_ERASE); ++ return sfl->blocksize; ++ } ++ ++ return 0; ++} +--- a/drivers/bcma/driver_mips.c ++++ b/drivers/bcma/driver_mips.c +@@ -185,7 +185,13 @@ static void bcma_core_mips_flash_detect( + switch (bus->drv_cc.capabilities & BCMA_CC_CAP_FLASHT) { + case BCMA_CC_FLASHT_STSER: + case BCMA_CC_FLASHT_ATSER: +- bcma_err(bus, "Serial flash not supported.\n"); ++#ifdef CONFIG_BCMA_SFLASH ++ bcma_info(bus, "found serial flash.\n"); ++ bus->drv_cc.flash_type = BCMA_SFLASH; ++ bcma_sflash_init(&bus->drv_cc); ++#else ++ bcma_info(bus, "serial flash not supported.\n"); ++#endif /* CONFIG_BCMA_SFLASH */ + break; + case BCMA_CC_FLASHT_PARA: + bcma_info(bus, "found parallel flash.\n"); +--- a/include/linux/bcma/bcma_driver_chipcommon.h ++++ b/include/linux/bcma/bcma_driver_chipcommon.h +@@ -435,6 +435,7 @@ struct bcma_chipcommon_pmu { + #ifdef CONFIG_BCMA_DRIVER_MIPS + enum bcma_flash_type { + BCMA_PFLASH, ++ BCMA_SFLASH, + }; + + struct bcma_pflash { +@@ -443,6 +444,14 @@ struct bcma_pflash { + u32 window_size; + }; + ++#ifdef CONFIG_BCMA_SFLASH ++struct bcma_sflash { ++ u32 blocksize; /* Block size */ ++ u32 numblocks; /* Number of blocks */ ++ u32 size; /* Total size in bytes */ ++}; ++#endif /* CONFIG_BCMA_SFLASH */ ++ + struct bcma_serial_port { + void *regs; + unsigned long clockspeed; +@@ -465,6 +474,9 @@ struct bcma_drv_cc { + enum bcma_flash_type flash_type; + union { + struct bcma_pflash pflash; ++#ifdef CONFIG_BCMA_SFLASH ++ struct bcma_sflash sflash; ++#endif /* CONFIG_BCMA_SFLASH */ + }; + + int nr_serial_ports; +@@ -520,4 +532,14 @@ extern void bcma_chipco_regctl_maskset(s + u32 offset, u32 mask, u32 set); + extern void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid); + ++#ifdef CONFIG_BCMA_SFLASH ++/* Chipcommon sflash support. */ ++int bcma_sflash_read(struct bcma_drv_cc *cc, u32 offset, u32 len, ++ u8 *buf); ++int bcma_sflash_poll(struct bcma_drv_cc *cc, u32 offset); ++int bcma_sflash_write(struct bcma_drv_cc *cc, u32 offset, u32 len, ++ const u8 *buf); ++int bcma_sflash_erase(struct bcma_drv_cc *cc, u32 offset); ++#endif /* CONFIG_BCMA_SFLASH */ ++ + #endif /* LINUX_BCMA_DRIVER_CC_H_ */ diff --git a/target/linux/brcm47xx/patches-3.3/022-ssb-move-flash-to-chipcommon.patch b/target/linux/brcm47xx/patches-3.3/022-ssb-move-flash-to-chipcommon.patch new file mode 100644 index 000000000..12055bff0 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/022-ssb-move-flash-to-chipcommon.patch @@ -0,0 +1,151 @@ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -27,7 +27,7 @@ static char nvram_buf[NVRAM_SPACE]; + static void early_nvram_init(void) + { + #ifdef CONFIG_BCM47XX_SSB +- struct ssb_mipscore *mcore_ssb; ++ struct ssb_chipcommon *ssb_cc; + #endif + #ifdef CONFIG_BCM47XX_BCMA + struct bcma_drv_cc *bcma_cc; +@@ -42,9 +42,9 @@ static void early_nvram_init(void) + switch (bcm47xx_bus_type) { + #ifdef CONFIG_BCM47XX_SSB + case BCM47XX_BUS_TYPE_SSB: +- mcore_ssb = &bcm47xx_bus.ssb.mipscore; +- base = mcore_ssb->flash_window; +- lim = mcore_ssb->flash_window_size; ++ ssb_cc = &bcm47xx_bus.ssb.chipco; ++ base = ssb_cc->pflash.window; ++ lim = ssb_cc->pflash.window_size; + break; + #endif + #ifdef CONFIG_BCM47XX_BCMA +--- a/arch/mips/bcm47xx/wgt634u.c ++++ b/arch/mips/bcm47xx/wgt634u.c +@@ -142,24 +142,24 @@ static int __init wgt634u_init(void) + if (et0mac[0] == 0x00 && + ((et0mac[1] == 0x09 && et0mac[2] == 0x5b) || + (et0mac[1] == 0x0f && et0mac[2] == 0xb5))) { +- struct ssb_mipscore *mcore = &bcm47xx_bus.ssb.mipscore; ++ struct ssb_chipcommon *ccore = &bcm47xx_bus.ssb.chipco; + + printk(KERN_INFO "WGT634U machine detected.\n"); + + if (!request_irq(gpio_to_irq(WGT634U_GPIO_RESET), + gpio_interrupt, IRQF_SHARED, +- "WGT634U GPIO", &bcm47xx_bus.ssb.chipco)) { ++ "WGT634U GPIO", ccore)) { + gpio_direction_input(WGT634U_GPIO_RESET); + gpio_intmask(WGT634U_GPIO_RESET, 1); +- ssb_chipco_irq_mask(&bcm47xx_bus.ssb.chipco, ++ ssb_chipco_irq_mask(ccore, + SSB_CHIPCO_IRQ_GPIO, + SSB_CHIPCO_IRQ_GPIO); + } + +- wgt634u_flash_data.width = mcore->flash_buswidth; +- wgt634u_flash_resource.start = mcore->flash_window; +- wgt634u_flash_resource.end = mcore->flash_window +- + mcore->flash_window_size ++ wgt634u_flash_data.width = ccore->pflash.buswidth; ++ wgt634u_flash_resource.start = ccore->pflash.window; ++ wgt634u_flash_resource.end = ccore->pflash.window ++ + ccore->pflash.window_size + - 1; + return platform_add_devices(wgt634u_devices, + ARRAY_SIZE(wgt634u_devices)); +--- a/drivers/ssb/driver_mipscore.c ++++ b/drivers/ssb/driver_mipscore.c +@@ -190,16 +190,34 @@ static void ssb_mips_flash_detect(struct + { + struct ssb_bus *bus = mcore->dev->bus; + +- mcore->flash_buswidth = 2; +- if (bus->chipco.dev) { +- mcore->flash_window = 0x1c000000; +- mcore->flash_window_size = 0x02000000; ++ /* When there is no chipcommon on the bus there is 4MB flash */ ++ if (!bus->chipco.dev) { ++ pr_info("found parallel flash.\n"); ++ bus->chipco.flash_type = SSB_PFLASH; ++ bus->chipco.pflash.window = SSB_FLASH1; ++ bus->chipco.pflash.window_size = SSB_FLASH1_SZ; ++ bus->chipco.pflash.buswidth = 2; ++ return; ++ } ++ ++ switch (bus->chipco.capabilities & SSB_CHIPCO_CAP_FLASHT) { ++ case SSB_CHIPCO_FLASHT_STSER: ++ case SSB_CHIPCO_FLASHT_ATSER: ++ pr_info("serial flash not supported.\n"); ++ break; ++ case SSB_CHIPCO_FLASHT_PARA: ++ pr_info("found parallel flash.\n"); ++ bus->chipco.flash_type = SSB_PFLASH; ++ bus->chipco.pflash.window = SSB_FLASH2; ++ bus->chipco.pflash.window_size = SSB_FLASH2_SZ; + if ((ssb_read32(bus->chipco.dev, SSB_CHIPCO_FLASH_CFG) +- & SSB_CHIPCO_CFG_DS16) == 0) +- mcore->flash_buswidth = 1; +- } else { +- mcore->flash_window = 0x1fc00000; +- mcore->flash_window_size = 0x00400000; ++ & SSB_CHIPCO_CFG_DS16) == 0) ++ bus->chipco.pflash.buswidth = 1; ++ else ++ bus->chipco.pflash.buswidth = 2; ++ break; ++ default: ++ pr_err("flash not supported.\n"); + } + } + +--- a/include/linux/ssb/ssb_driver_chipcommon.h ++++ b/include/linux/ssb/ssb_driver_chipcommon.h +@@ -582,6 +582,18 @@ struct ssb_chipcommon_pmu { + u32 crystalfreq; /* The active crystal frequency (in kHz) */ + }; + ++#ifdef CONFIG_SSB_DRIVER_MIPS ++enum ssb_flash_type { ++ SSB_PFLASH, ++}; ++ ++struct ssb_pflash { ++ u8 buswidth; ++ u32 window; ++ u32 window_size; ++}; ++#endif /* CONFIG_SSB_DRIVER_MIPS */ ++ + struct ssb_chipcommon { + struct ssb_device *dev; + u32 capabilities; +@@ -589,6 +601,12 @@ struct ssb_chipcommon { + /* Fast Powerup Delay constant */ + u16 fast_pwrup_delay; + struct ssb_chipcommon_pmu pmu; ++#ifdef CONFIG_SSB_DRIVER_MIPS ++ enum ssb_flash_type flash_type; ++ union { ++ struct ssb_pflash pflash; ++ }; ++#endif /* CONFIG_SSB_DRIVER_MIPS */ + }; + + static inline bool ssb_chipco_available(struct ssb_chipcommon *cc) +--- a/include/linux/ssb/ssb_driver_mips.h ++++ b/include/linux/ssb/ssb_driver_mips.h +@@ -19,10 +19,6 @@ struct ssb_mipscore { + + int nr_serial_ports; + struct ssb_serial_port serial_ports[4]; +- +- u8 flash_buswidth; +- u32 flash_window; +- u32 flash_window_size; + }; + + extern void ssb_mipscore_init(struct ssb_mipscore *mcore); diff --git a/target/linux/brcm47xx/patches-3.3/023-ssb-add-serial-flash-support.patch b/target/linux/brcm47xx/patches-3.3/023-ssb-add-serial-flash-support.patch new file mode 100644 index 000000000..ba5a5c2d1 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/023-ssb-add-serial-flash-support.patch @@ -0,0 +1,573 @@ +--- a/drivers/ssb/Kconfig ++++ b/drivers/ssb/Kconfig +@@ -137,6 +137,12 @@ config SSB_DRIVER_MIPS + + If unsure, say N + ++config SSB_SFLASH ++ bool ++ depends on SSB_DRIVER_MIPS ++ default y ++ ++ + # Assumption: We are on embedded, if we compile the MIPS core. + config SSB_EMBEDDED + bool +--- a/drivers/ssb/Makefile ++++ b/drivers/ssb/Makefile +@@ -11,6 +11,7 @@ ssb-$(CONFIG_SSB_SDIOHOST) += sdio.o + # built-in drivers + ssb-y += driver_chipcommon.o + ssb-y += driver_chipcommon_pmu.o ++ssb-$(CONFIG_SSB_SFLASH) += driver_chipcommon_sflash.o + ssb-$(CONFIG_SSB_DRIVER_MIPS) += driver_mipscore.o + ssb-$(CONFIG_SSB_DRIVER_EXTIF) += driver_extif.o + ssb-$(CONFIG_SSB_DRIVER_PCICORE) += driver_pcicore.o +--- /dev/null ++++ b/drivers/ssb/driver_chipcommon_sflash.c +@@ -0,0 +1,451 @@ ++/* ++ * Broadcom SiliconBackplane chipcommon serial flash interface ++ * ++ * Copyright 2011, Jonas Gorski <jonas.gorski@gmail.com> ++ * Copyright 2011, 2012, Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright 2010, Broadcom Corporation ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++ ++#include <linux/ssb/ssb.h> ++#include <linux/ssb/ssb_driver_chipcommon.h> ++#include <linux/delay.h> ++ ++#include "ssb_private.h" ++ ++#define NUM_RETRIES 3 ++ ++ ++/* Issue a serial flash command */ ++static inline void ssb_sflash_cmd(struct ssb_chipcommon *cc, u32 opcode) ++{ ++ chipco_write32(cc, SSB_CHIPCO_FLASHCTL, ++ SSB_CHIPCO_FLASHCTL_START | opcode); ++ while (chipco_read32(cc, SSB_CHIPCO_FLASHCTL) ++ & SSB_CHIPCO_FLASHCTL_BUSY) ++ ; ++} ++ ++ ++static inline void ssb_sflash_write_u8(struct ssb_chipcommon *cc, ++ u32 offset, u8 byte) ++{ ++ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, offset); ++ chipco_write32(cc, SSB_CHIPCO_FLASHDATA, byte); ++} ++ ++/* Initialize serial flash access */ ++int ssb_sflash_init(struct ssb_chipcommon *cc) ++{ ++ u32 id, id2; ++ ++ memset(&cc->sflash, 0, sizeof(struct ssb_sflash)); ++ ++ switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) { ++ case SSB_CHIPCO_FLASHT_STSER: ++ /* Probe for ST chips */ ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_DP); ++ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, 0); ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_RES); ++ id = chipco_read32(cc, SSB_CHIPCO_FLASHDATA); ++ cc->sflash.blocksize = 64 * 1024; ++ switch (id) { ++ case 0x11: ++ /* ST M25P20 2 Mbit Serial Flash */ ++ cc->sflash.numblocks = 4; ++ break; ++ case 0x12: ++ /* ST M25P40 4 Mbit Serial Flash */ ++ cc->sflash.numblocks = 8; ++ break; ++ case 0x13: ++ /* ST M25P80 8 Mbit Serial Flash */ ++ cc->sflash.numblocks = 16; ++ break; ++ case 0x14: ++ /* ST M25P16 16 Mbit Serial Flash */ ++ cc->sflash.numblocks = 32; ++ break; ++ case 0x15: ++ /* ST M25P32 32 Mbit Serial Flash */ ++ cc->sflash.numblocks = 64; ++ break; ++ case 0x16: ++ /* ST M25P64 64 Mbit Serial Flash */ ++ cc->sflash.numblocks = 128; ++ break; ++ case 0x17: ++ /* ST M25FL128 128 Mbit Serial Flash */ ++ cc->sflash.numblocks = 256; ++ break; ++ case 0xbf: ++ /* All of the following flashes are SST with ++ * 4KB subsectors. Others should be added but ++ * We'll have to revamp the way we identify them ++ * since RES is not eough to disambiguate them. ++ */ ++ cc->sflash.blocksize = 4 * 1024; ++ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, 1); ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_RES); ++ id2 = chipco_read32(cc, SSB_CHIPCO_FLASHDATA); ++ switch (id2) { ++ case 1: ++ /* SST25WF512 512 Kbit Serial Flash */ ++ case 0x48: ++ /* SST25VF512 512 Kbit Serial Flash */ ++ cc->sflash.numblocks = 16; ++ break; ++ case 2: ++ /* SST25WF010 1 Mbit Serial Flash */ ++ case 0x49: ++ /* SST25VF010 1 Mbit Serial Flash */ ++ cc->sflash.numblocks = 32; ++ break; ++ case 3: ++ /* SST25WF020 2 Mbit Serial Flash */ ++ case 0x43: ++ /* SST25VF020 2 Mbit Serial Flash */ ++ cc->sflash.numblocks = 64; ++ break; ++ case 4: ++ /* SST25WF040 4 Mbit Serial Flash */ ++ case 0x44: ++ /* SST25VF040 4 Mbit Serial Flash */ ++ case 0x8d: ++ /* SST25VF040B 4 Mbit Serial Flash */ ++ cc->sflash.numblocks = 128; ++ break; ++ case 5: ++ /* SST25WF080 8 Mbit Serial Flash */ ++ case 0x8e: ++ /* SST25VF080B 8 Mbit Serial Flash */ ++ cc->sflash.numblocks = 256; ++ break; ++ case 0x41: ++ /* SST25VF016 16 Mbit Serial Flash */ ++ cc->sflash.numblocks = 512; ++ break; ++ case 0x4a: ++ /* SST25VF032 32 Mbit Serial Flash */ ++ cc->sflash.numblocks = 1024; ++ break; ++ case 0x4b: ++ /* SST25VF064 64 Mbit Serial Flash */ ++ cc->sflash.numblocks = 2048; ++ break; ++ } ++ break; ++ } ++ break; ++ ++ case SSB_CHIPCO_FLASHT_ATSER: ++ /* Probe for Atmel chips */ ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_STATUS); ++ id = chipco_read32(cc, SSB_CHIPCO_FLASHDATA) & 0x3c; ++ switch (id) { ++ case 0xc: ++ /* Atmel AT45DB011 1Mbit Serial Flash */ ++ cc->sflash.blocksize = 256; ++ cc->sflash.numblocks = 512; ++ break; ++ case 0x14: ++ /* Atmel AT45DB021 2Mbit Serial Flash */ ++ cc->sflash.blocksize = 256; ++ cc->sflash.numblocks = 1024; ++ break; ++ case 0x1c: ++ /* Atmel AT45DB041 4Mbit Serial Flash */ ++ cc->sflash.blocksize = 256; ++ cc->sflash.numblocks = 2048; ++ break; ++ case 0x24: ++ /* Atmel AT45DB081 8Mbit Serial Flash */ ++ cc->sflash.blocksize = 256; ++ cc->sflash.numblocks = 4096; ++ break; ++ case 0x2c: ++ /* Atmel AT45DB161 16Mbit Serial Flash */ ++ cc->sflash.blocksize = 512; ++ cc->sflash.numblocks = 4096; ++ break; ++ case 0x34: ++ /* Atmel AT45DB321 32Mbit Serial Flash */ ++ cc->sflash.blocksize = 512; ++ cc->sflash.numblocks = 8192; ++ break; ++ case 0x3c: ++ /* Atmel AT45DB642 64Mbit Serial Flash */ ++ cc->sflash.blocksize = 1024; ++ cc->sflash.numblocks = 8192; ++ break; ++ } ++ break; ++ } ++ ++ cc->sflash.size = cc->sflash.blocksize * cc->sflash.numblocks; ++ ++ return cc->sflash.size ? 0 : -ENODEV; ++} ++ ++/* Read len bytes starting at offset into buf. Returns number of bytes read. */ ++int ssb_sflash_read(struct ssb_chipcommon *cc, u32 offset, u32 len, u8 *buf) ++{ ++ u8 *from, *to; ++ u32 cnt, i; ++ ++ if (!len) ++ return 0; ++ ++ if ((offset + len) > cc->sflash.size) ++ return -EINVAL; ++ ++ if ((len >= 4) && (offset & 3)) ++ cnt = 4 - (offset & 3); ++ else if ((len >= 4) && ((u32)buf & 3)) ++ cnt = 4 - ((u32)buf & 3); ++ else ++ cnt = len; ++ ++ ++ if (cc->dev->id.revision == 12) ++ from = (u8 *)KSEG1ADDR(SSB_FLASH2 + offset); ++ else ++ from = (u8 *)KSEG0ADDR(SSB_FLASH2 + offset); ++ ++ to = (u8 *)buf; ++ ++ if (cnt < 4) { ++ for (i = 0; i < cnt; i++) { ++ *to = readb(from); ++ from++; ++ to++; ++ } ++ return cnt; ++ } ++ ++ while (cnt >= 4) { ++ *(u32 *)to = readl(from); ++ from += 4; ++ to += 4; ++ cnt -= 4; ++ } ++ ++ return len - cnt; ++} ++ ++/* Poll for command completion. Returns zero when complete. */ ++int ssb_sflash_poll(struct ssb_chipcommon *cc, u32 offset) ++{ ++ if (offset >= cc->sflash.size) ++ return -22; ++ ++ switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) { ++ case SSB_CHIPCO_FLASHT_STSER: ++ /* Check for ST Write In Progress bit */ ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_RDSR); ++ return chipco_read32(cc, SSB_CHIPCO_FLASHDATA) ++ & SSB_CHIPCO_FLASHSTA_ST_WIP; ++ case SSB_CHIPCO_FLASHT_ATSER: ++ /* Check for Atmel Ready bit */ ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_STATUS); ++ return !(chipco_read32(cc, SSB_CHIPCO_FLASHDATA) ++ & SSB_CHIPCO_FLASHSTA_AT_READY); ++ } ++ ++ return 0; ++} ++ ++ ++static int sflash_st_write(struct ssb_chipcommon *cc, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ struct ssb_bus *bus = cc->dev->bus; ++ int ret = 0; ++ bool is4712b0 = (bus->chip_id == 0x4712) && (bus->chip_rev == 3); ++ u32 mask; ++ ++ /* Enable writes */ ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_WREN); ++ if (is4712b0) { ++ mask = 1 << 14; ++ ssb_sflash_write_u8(cc, offset, *buf++); ++ /* Set chip select */ ++ chipco_set32(cc, SSB_CHIPCO_GPIOOUT, mask); ++ /* Issue a page program with the first byte */ ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_PP); ++ ret = 1; ++ offset++; ++ len--; ++ while (len > 0) { ++ if ((offset & 255) == 0) { ++ /* Page boundary, drop cs and return */ ++ chipco_mask32(cc, SSB_CHIPCO_GPIOOUT, ~mask); ++ udelay(1); ++ if (!ssb_sflash_poll(cc, offset)) { ++ /* Flash rejected command */ ++ return -EAGAIN; ++ } ++ return ret; ++ } else { ++ /* Write single byte */ ++ ssb_sflash_cmd(cc, *buf++); ++ } ++ ret++; ++ offset++; ++ len--; ++ } ++ /* All done, drop cs */ ++ chipco_mask32(cc, SSB_CHIPCO_GPIOOUT, ~mask); ++ udelay(1); ++ if (!ssb_sflash_poll(cc, offset)) { ++ /* Flash rejected command */ ++ return -EAGAIN; ++ } ++ } else if (cc->dev->id.revision >= 20) { ++ ssb_sflash_write_u8(cc, offset, *buf++); ++ /* Issue a page program with CSA bit set */ ++ ssb_sflash_cmd(cc, ++ SSB_CHIPCO_FLASHCTL_ST_CSA | ++ SSB_CHIPCO_FLASHCTL_ST_PP); ++ ret = 1; ++ offset++; ++ len--; ++ while (len > 0) { ++ if ((offset & 255) == 0) { ++ /* Page boundary, poll droping cs and return */ ++ chipco_write32(cc, SSB_CHIPCO_FLASHCTL, 0); ++ udelay(1); ++ if (!ssb_sflash_poll(cc, offset)) { ++ /* Flash rejected command */ ++ return -EAGAIN; ++ } ++ return ret; ++ } else { ++ /* Write single byte */ ++ ssb_sflash_cmd(cc, ++ SSB_CHIPCO_FLASHCTL_ST_CSA | ++ *buf++); ++ } ++ ret++; ++ offset++; ++ len--; ++ } ++ /* All done, drop cs & poll */ ++ chipco_write32(cc, SSB_CHIPCO_FLASHCTL, 0); ++ udelay(1); ++ if (!ssb_sflash_poll(cc, offset)) { ++ /* Flash rejected command */ ++ return -EAGAIN; ++ } ++ } else { ++ ret = 1; ++ ssb_sflash_write_u8(cc, offset, *buf); ++ /* Page program */ ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_PP); ++ } ++ return ret; ++} ++ ++static int sflash_at_write(struct ssb_chipcommon *cc, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ struct ssb_sflash *sfl = &cc->sflash; ++ u32 page, byte, mask; ++ int ret = 0; ++ mask = sfl->blocksize - 1; ++ page = (offset & ~mask) << 1; ++ byte = offset & mask; ++ /* Read main memory page into buffer 1 */ ++ if (byte || (len < sfl->blocksize)) { ++ int i = 100; ++ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, page); ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_BUF1_LOAD); ++ /* 250 us for AT45DB321B */ ++ while (i > 0 && ssb_sflash_poll(cc, offset)) { ++ udelay(10); ++ i--; ++ } ++ BUG_ON(!ssb_sflash_poll(cc, offset)); ++ } ++ /* Write into buffer 1 */ ++ for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) { ++ ssb_sflash_write_u8(cc, byte++, *buf++); ++ ssb_sflash_cmd(cc, ++ SSB_CHIPCO_FLASHCTL_AT_BUF1_WRITE); ++ } ++ /* Write buffer 1 into main memory page */ ++ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, page); ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_BUF1_PROGRAM); ++ ++ return ret; ++} ++ ++/* Write len bytes starting at offset into buf. Returns number of bytes ++ * written. Caller should poll for completion. ++ */ ++int ssb_sflash_write(struct ssb_chipcommon *cc, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ struct ssb_sflash *sfl; ++ int ret = 0, tries = NUM_RETRIES; ++ ++ if (!len) ++ return 0; ++ ++ if ((offset + len) > cc->sflash.size) ++ return -EINVAL; ++ ++ sfl = &cc->sflash; ++ switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) { ++ case SSB_CHIPCO_FLASHT_STSER: ++ do { ++ ret = sflash_st_write(cc, offset, len, buf); ++ tries--; ++ } while (ret == -EAGAIN && tries > 0); ++ ++ if (ret == -EAGAIN && tries == 0) { ++ pr_info("ST Flash rejected write\n"); ++ ret = -EIO; ++ } ++ break; ++ case SSB_CHIPCO_FLASHT_ATSER: ++ ret = sflash_at_write(cc, offset, len, buf); ++ break; ++ } ++ ++ return ret; ++} ++ ++/* Erase a region. Returns number of bytes scheduled for erasure. ++ * Caller should poll for completion. ++ */ ++int ssb_sflash_erase(struct ssb_chipcommon *cc, u32 offset) ++{ ++ struct ssb_sflash *sfl; ++ ++ if (offset >= cc->sflash.size) ++ return -EINVAL; ++ ++ sfl = &cc->sflash; ++ switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) { ++ case SSB_CHIPCO_FLASHT_STSER: ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_WREN); ++ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, offset); ++ /* Newer flashes have "sub-sectors" which can be erased ++ * independently with a new command: ST_SSE. The ST_SE command ++ * erases 64KB just as before. ++ */ ++ if (sfl->blocksize < (64 * 1024)) ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_SSE); ++ else ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_SE); ++ return sfl->blocksize; ++ case SSB_CHIPCO_FLASHT_ATSER: ++ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, offset << 1); ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_PAGE_ERASE); ++ return sfl->blocksize; ++ } ++ ++ return 0; ++} +--- a/drivers/ssb/driver_mipscore.c ++++ b/drivers/ssb/driver_mipscore.c +@@ -203,7 +203,13 @@ static void ssb_mips_flash_detect(struct + switch (bus->chipco.capabilities & SSB_CHIPCO_CAP_FLASHT) { + case SSB_CHIPCO_FLASHT_STSER: + case SSB_CHIPCO_FLASHT_ATSER: ++#ifdef CONFIG_SSB_SFLASH ++ pr_info("found serial flash.\n"); ++ bus->chipco.flash_type = SSB_SFLASH; ++ ssb_sflash_init(&bus->chipco); ++#else + pr_info("serial flash not supported.\n"); ++#endif /* CONFIG_SSB_SFLASH */ + break; + case SSB_CHIPCO_FLASHT_PARA: + pr_info("found parallel flash.\n"); +--- a/drivers/ssb/ssb_private.h ++++ b/drivers/ssb/ssb_private.h +@@ -192,6 +192,10 @@ extern int ssb_devices_freeze(struct ssb + extern int ssb_devices_thaw(struct ssb_freeze_context *ctx); + + ++#ifdef CONFIG_SSB_SFLASH ++/* driver_chipcommon_sflash.c */ ++int ssb_sflash_init(struct ssb_chipcommon *cc); ++#endif /* CONFIG_SSB_SFLASH */ + + /* b43_pci_bridge.c */ + #ifdef CONFIG_SSB_B43_PCI_BRIDGE +--- a/include/linux/ssb/ssb_driver_chipcommon.h ++++ b/include/linux/ssb/ssb_driver_chipcommon.h +@@ -503,8 +503,10 @@ + #define SSB_CHIPCO_FLASHCTL_ST_PP 0x0302 /* Page Program */ + #define SSB_CHIPCO_FLASHCTL_ST_SE 0x02D8 /* Sector Erase */ + #define SSB_CHIPCO_FLASHCTL_ST_BE 0x00C7 /* Bulk Erase */ +-#define SSB_CHIPCO_FLASHCTL_ST_DP 0x00B9 /* Deep Power-down */ +-#define SSB_CHIPCO_FLASHCTL_ST_RSIG 0x03AB /* Read Electronic Signature */ ++#define SSB_CHIPCO_FLASHCTL_ST_DP 0x00D9 /* Deep Power-down */ ++#define SSB_CHIPCO_FLASHCTL_ST_RES 0x03AB /* Read Electronic Signature */ ++#define SSB_CHIPCO_FLASHCTL_ST_CSA 0x1000 /* Keep chip select asserted */ ++#define SSB_CHIPCO_FLASHCTL_ST_SSE 0x0220 /* Sub-sector Erase */ + + /* Status register bits for ST flashes */ + #define SSB_CHIPCO_FLASHSTA_ST_WIP 0x01 /* Write In Progress */ +@@ -585,6 +587,7 @@ struct ssb_chipcommon_pmu { + #ifdef CONFIG_SSB_DRIVER_MIPS + enum ssb_flash_type { + SSB_PFLASH, ++ SSB_SFLASH, + }; + + struct ssb_pflash { +@@ -592,6 +595,14 @@ struct ssb_pflash { + u32 window; + u32 window_size; + }; ++ ++#ifdef CONFIG_SSB_SFLASH ++struct ssb_sflash { ++ u32 blocksize; /* Block size */ ++ u32 numblocks; /* Number of blocks */ ++ u32 size; /* Total size in bytes */ ++}; ++#endif /* CONFIG_SSB_SFLASH */ + #endif /* CONFIG_SSB_DRIVER_MIPS */ + + struct ssb_chipcommon { +@@ -605,6 +616,9 @@ struct ssb_chipcommon { + enum ssb_flash_type flash_type; + union { + struct ssb_pflash pflash; ++#ifdef CONFIG_SSB_SFLASH ++ struct ssb_sflash sflash; ++#endif /* CONFIG_SSB_SFLASH */ + }; + #endif /* CONFIG_SSB_DRIVER_MIPS */ + }; +@@ -666,6 +680,16 @@ extern int ssb_chipco_serial_init(struct + struct ssb_serial_port *ports); + #endif /* CONFIG_SSB_SERIAL */ + ++#ifdef CONFIG_SSB_SFLASH ++/* Chipcommon sflash support. */ ++int ssb_sflash_read(struct ssb_chipcommon *cc, u32 offset, u32 len, ++ u8 *buf); ++int ssb_sflash_poll(struct ssb_chipcommon *cc, u32 offset); ++int ssb_sflash_write(struct ssb_chipcommon *cc, u32 offset, u32 len, ++ const u8 *buf); ++int ssb_sflash_erase(struct ssb_chipcommon *cc, u32 offset); ++#endif /* CONFIG_SSB_SFLASH */ ++ + /* PMU support */ + extern void ssb_pmu_init(struct ssb_chipcommon *cc); + diff --git a/target/linux/brcm47xx/patches-3.3/024-brcm47xx-add-common-interface-for-sflash.patch b/target/linux/brcm47xx/patches-3.3/024-brcm47xx-add-common-interface-for-sflash.patch new file mode 100644 index 000000000..fd4befcfa --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/024-brcm47xx-add-common-interface-for-sflash.patch @@ -0,0 +1,168 @@ +--- a/arch/mips/bcm47xx/Makefile ++++ b/arch/mips/bcm47xx/Makefile +@@ -3,5 +3,5 @@ + # under Linux. + # + +-obj-y += gpio.o irq.o nvram.o prom.o serial.o setup.o time.o sprom.o ++obj-y += gpio.o irq.o nvram.o prom.o serial.o setup.o time.o sprom.o bus.o + obj-$(CONFIG_BCM47XX_SSB) += wgt634u.o +--- /dev/null ++++ b/arch/mips/bcm47xx/bus.c +@@ -0,0 +1,86 @@ ++/* ++ * BCM947xx nvram variable access ++ * ++ * Copyright (C) 2011 Hauke Mehrtens <hauke@hauke-m.de> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include <bus.h> ++ ++#ifdef CONFIG_BCM47XX_BCMA ++static int bcm47xx_sflash_bcma_read(struct bcm47xx_sflash *dev, u32 offset, u32 len, u8 *buf) ++{ ++ return bcma_sflash_read(dev->bcc, offset, len, buf); ++} ++ ++static int bcm47xx_sflash_bcma_poll(struct bcm47xx_sflash *dev, u32 offset) ++{ ++ return bcma_sflash_poll(dev->bcc, offset); ++} ++ ++static int bcm47xx_sflash_bcma_write(struct bcm47xx_sflash *dev, u32 offset, u32 len, const u8 *buf) ++{ ++ return bcma_sflash_write(dev->bcc, offset, len, buf); ++} ++ ++static int bcm47xx_sflash_bcma_erase(struct bcm47xx_sflash *dev, u32 offset) ++{ ++ return bcma_sflash_erase(dev->bcc, offset); ++} ++ ++void bcm47xx_sflash_struct_bcma_init(struct bcm47xx_sflash *sflash, struct bcma_drv_cc *bcc) ++{ ++ sflash->sflash_type = BCM47XX_BUS_TYPE_BCMA; ++ sflash->bcc = bcc; ++ ++ sflash->read = bcm47xx_sflash_bcma_read; ++ sflash->poll = bcm47xx_sflash_bcma_poll; ++ sflash->write = bcm47xx_sflash_bcma_write; ++ sflash->erase = bcm47xx_sflash_bcma_erase; ++ ++ sflash->blocksize = bcc->sflash.blocksize; ++ sflash->numblocks = bcc->sflash.numblocks; ++ sflash->size = bcc->sflash.size; ++} ++#endif ++ ++#ifdef CONFIG_BCM47XX_SSB ++static int bcm47xx_sflash_ssb_read(struct bcm47xx_sflash *dev, u32 offset, u32 len, u8 *buf) ++{ ++ return ssb_sflash_read(dev->scc, offset, len, buf); ++} ++ ++static int bcm47xx_sflash_ssb_poll(struct bcm47xx_sflash *dev, u32 offset) ++{ ++ return ssb_sflash_poll(dev->scc, offset); ++} ++ ++static int bcm47xx_sflash_ssb_write(struct bcm47xx_sflash *dev, u32 offset, u32 len, const u8 *buf) ++{ ++ return ssb_sflash_write(dev->scc, offset, len, buf); ++} ++ ++static int bcm47xx_sflash_ssb_erase(struct bcm47xx_sflash *dev, u32 offset) ++{ ++ return ssb_sflash_erase(dev->scc, offset); ++} ++ ++void bcm47xx_sflash_struct_ssb_init(struct bcm47xx_sflash *sflash, struct ssb_chipcommon *scc) ++{ ++ sflash->sflash_type = BCM47XX_BUS_TYPE_SSB; ++ sflash->scc = scc; ++ ++ sflash->read = bcm47xx_sflash_ssb_read; ++ sflash->poll = bcm47xx_sflash_ssb_poll; ++ sflash->write = bcm47xx_sflash_ssb_write; ++ sflash->erase = bcm47xx_sflash_ssb_erase; ++ ++ sflash->blocksize = scc->sflash.blocksize; ++ sflash->numblocks = scc->sflash.numblocks; ++ sflash->size = scc->sflash.size; ++} ++#endif +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -43,6 +43,8 @@ EXPORT_SYMBOL(bcm47xx_bus); + enum bcm47xx_bus_type bcm47xx_bus_type; + EXPORT_SYMBOL(bcm47xx_bus_type); + ++struct bcm47xx_sflash bcm47xx_sflash; ++ + static void bcm47xx_machine_restart(char *command) + { + printk(KERN_ALERT "Please stand by while rebooting the system...\n"); +@@ -137,6 +139,9 @@ static void __init bcm47xx_register_ssb( + if (err) + panic("Failed to initialize SSB bus (err %d)", err); + ++ if (bcm47xx_bus.ssb.chipco.flash_type == SSB_SFLASH) ++ bcm47xx_sflash_struct_ssb_init(&bcm47xx_sflash, &bcm47xx_bus.ssb.chipco); ++ + mcore = &bcm47xx_bus.ssb.mipscore; + if (nvram_getenv("kernel_args", buf, sizeof(buf)) >= 0) { + if (strstr(buf, "console=ttyS1")) { +@@ -195,6 +200,9 @@ static void __init bcm47xx_register_bcma + if (err) + panic("Failed to initialize BCMA bus (err %d)", err); + ++ if (bcm47xx_bus.bcma.bus.drv_cc.flash_type == BCMA_SFLASH) ++ bcm47xx_sflash_struct_bcma_init(&bcm47xx_sflash, &bcm47xx_bus.bcma.bus.drv_cc); ++ + bcm47xx_fill_bcma_boardinfo(&bcm47xx_bus.bcma.bus.boardinfo, NULL); + } + #endif +--- /dev/null ++++ b/arch/mips/include/asm/mach-bcm47xx/bus.h +@@ -0,0 +1,36 @@ ++/* ++ * BCM947xx nvram variable access ++ * ++ * Copyright (C) 2011 Hauke Mehrtens <hauke@hauke-m.de> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include <linux/ssb/ssb.h> ++#include <linux/bcma/bcma.h> ++#include <bcm47xx.h> ++ ++struct bcm47xx_sflash { ++ enum bcm47xx_bus_type sflash_type; ++ union { ++ struct ssb_chipcommon *scc; ++ struct bcma_drv_cc *bcc; ++ }; ++ ++ int (*read)(struct bcm47xx_sflash *dev, u32 offset, u32 len, u8 *buf); ++ int (*poll)(struct bcm47xx_sflash *dev, u32 offset); ++ int (*write)(struct bcm47xx_sflash *dev, u32 offset, u32 len, const u8 *buf); ++ int (*erase)(struct bcm47xx_sflash *dev, u32 offset); ++ ++ u32 blocksize; /* Block size */ ++ u32 numblocks; /* Number of blocks */ ++ u32 size; /* Total size in bytes */ ++}; ++ ++void bcm47xx_sflash_struct_bcma_init(struct bcm47xx_sflash *sflash, struct bcma_drv_cc *bcc); ++void bcm47xx_sflash_struct_ssb_init(struct bcm47xx_sflash *sflash, struct ssb_chipcommon *scc); ++ ++extern struct bcm47xx_sflash bcm47xx_sflash; diff --git a/target/linux/brcm47xx/patches-3.3/025-mtd-bcm47xx-add-bcm47xx-part-parser.patch b/target/linux/brcm47xx/patches-3.3/025-mtd-bcm47xx-add-bcm47xx-part-parser.patch new file mode 100644 index 000000000..5381e2f6c --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/025-mtd-bcm47xx-add-bcm47xx-part-parser.patch @@ -0,0 +1,571 @@ +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -172,6 +172,13 @@ config MTD_MYLOADER_PARTS + You will still need the parsing functions to be called by the driver + for your particular device. It won't happen automatically. + ++config MTD_BCM47XX_PARTS ++ tristate "BCM47XX partitioning support" ++ default y ++ depends on BCM47XX ++ ---help--- ++ bcm47XX partitioning support ++ + comment "User Modules And Translation Layers" + + config MTD_CHAR +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -13,6 +13,7 @@ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o + obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o + obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o + obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o ++obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o + + # 'Users' - code which presents functionality to userspace. + obj-$(CONFIG_MTD_CHAR) += mtdchar.o +--- /dev/null ++++ b/drivers/mtd/bcm47xxpart.c +@@ -0,0 +1,542 @@ ++/* ++ * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> ++ * Copyright (C) 2005 Waldemar Brodkorb <wbx@openwrt.org> ++ * Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org) ++ * ++ * original functions for finding root filesystem from Mike Baker ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN ++ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ++ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ++ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Copyright 2001-2003, Broadcom Corporation ++ * All Rights Reserved. ++ * ++ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY ++ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM ++ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS ++ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. ++ * ++ * Flash mapping for BCM947XX boards ++ */ ++ ++#define pr_fmt(fmt) "bcm47xx_part: " fmt ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/wait.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/crc32.h> ++#include <linux/io.h> ++#include <asm/mach-bcm47xx/nvram.h> ++#include <asm/mach-bcm47xx/bcm47xx.h> ++#include <asm/fw/cfe/cfe_api.h> ++ ++ ++#define TRX_MAGIC 0x30524448 /* "HDR0" */ ++#define TRX_VERSION 1 ++#define TRX_MAX_LEN 0x3A0000 ++#define TRX_NO_HEADER 1 /* Do not write TRX header */ ++#define TRX_GZ_FILES 0x2 /* Contains up to TRX_MAX_OFFSET individual gzip files */ ++#define TRX_MAX_OFFSET 3 ++ ++struct trx_header { ++ u32 magic; /* "HDR0" */ ++ u32 len; /* Length of file including header */ ++ u32 crc32; /* 32-bit CRC from flag_version to end of file */ ++ u32 flag_version; /* 0:15 flags, 16:31 version */ ++ u32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of header */ ++}; ++ ++/* for Edimax Print servers which use an additional header ++ * then the firmware on flash looks like : ++ * EDIMAX HEADER | TRX HEADER ++ * As this header is 12 bytes long we have to handle it ++ * and skip it to find the TRX header ++ */ ++#define EDIMAX_PS_HEADER_MAGIC 0x36315350 /* "PS16" */ ++#define EDIMAX_PS_HEADER_LEN 0xc /* 12 bytes long for edimax header */ ++ ++#define NVRAM_SPACE 0x8000 ++ ++#define ROUTER_NETGEAR_WGR614L 1 ++#define ROUTER_NETGEAR_WNR834B 2 ++#define ROUTER_NETGEAR_WNDR3300 3 ++#define ROUTER_NETGEAR_WNR3500L 4 ++#define ROUTER_SIMPLETECH_SIMPLESHARE 5 ++#define ROUTER_NETGEAR_WNDR3400 6 ++ ++static int ++find_cfe_size(struct mtd_info *mtd) ++{ ++ struct trx_header *trx; ++ unsigned char buf[512]; ++ int off; ++ size_t len; ++ int blocksize; ++ ++ trx = (struct trx_header *) buf; ++ ++ blocksize = mtd->erasesize; ++ if (blocksize < 0x10000) ++ blocksize = 0x10000; ++ ++ for (off = (128*1024); off < mtd->size; off += blocksize) { ++ memset(buf, 0xe5, sizeof(buf)); ++ ++ /* ++ * Read into buffer ++ */ ++ if (mtd->read(mtd, off, sizeof(buf), &len, buf) || ++ len != sizeof(buf)) ++ continue; ++ ++ if (le32_to_cpu(trx->magic) == EDIMAX_PS_HEADER_MAGIC) { ++ if (mtd->read(mtd, off + EDIMAX_PS_HEADER_LEN, ++ sizeof(buf), &len, buf) || len != sizeof(buf)) { ++ continue; ++ } else { ++ pr_notice("Found edimax header\n"); ++ } ++ } ++ ++ /* found a TRX header */ ++ if (le32_to_cpu(trx->magic) == TRX_MAGIC) ++ goto found; ++ } ++ ++ pr_notice("%s: Couldn't find bootloader size\n", mtd->name); ++ return -1; ++ ++ found: ++ pr_notice("bootloader size: %d\n", off); ++ return off; ++ ++} ++ ++/* ++ * Copied from mtdblock.c ++ * ++ * Cache stuff... ++ * ++ * Since typical flash erasable sectors are much larger than what Linux's ++ * buffer cache can handle, we must implement read-modify-write on flash ++ * sectors for each block write requests. To avoid over-erasing flash sectors ++ * and to speed things up, we locally cache a whole flash sector while it is ++ * being written to until a different sector is required. ++ */ ++ ++static void erase_callback(struct erase_info *done) ++{ ++ wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; ++ wake_up(wait_q); ++} ++ ++static int erase_write(struct mtd_info *mtd, unsigned long pos, ++ int len, const char *buf) ++{ ++ struct erase_info erase; ++ DECLARE_WAITQUEUE(wait, current); ++ wait_queue_head_t wait_q; ++ size_t retlen; ++ int ret; ++ ++ /* ++ * First, let's erase the flash block. ++ */ ++ ++ init_waitqueue_head(&wait_q); ++ erase.mtd = mtd; ++ erase.callback = erase_callback; ++ erase.addr = pos; ++ erase.len = len; ++ erase.priv = (u_long)&wait_q; ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ add_wait_queue(&wait_q, &wait); ++ ++ ret = mtd->erase(mtd, &erase); ++ if (ret) { ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&wait_q, &wait); ++ pr_warn("erase of region [0x%lx, 0x%x] on \"%s\" failed\n", ++ pos, len, mtd->name); ++ return ret; ++ } ++ ++ schedule(); /* Wait for erase to finish. */ ++ remove_wait_queue(&wait_q, &wait); ++ ++ /* ++ * Next, write data to flash. ++ */ ++ ++ ret = mtd->write(mtd, pos, len, &retlen, buf); ++ if (ret) ++ return ret; ++ if (retlen != len) ++ return -EIO; ++ return 0; ++} ++ ++ ++static int ++find_dual_image_off(struct mtd_info *mtd) ++{ ++ struct trx_header trx; ++ int off, blocksize; ++ size_t len; ++ ++ blocksize = mtd->erasesize; ++ if (blocksize < 0x10000) ++ blocksize = 0x10000; ++ ++ for (off = (128*1024); off < mtd->size; off += blocksize) { ++ memset(&trx, 0xe5, sizeof(trx)); ++ /* ++ * Read into buffer ++ */ ++ if (mtd->read(mtd, off, sizeof(trx), &len, (char *) &trx) || ++ len != sizeof(trx)) ++ continue; ++ /* found last TRX header */ ++ if (le32_to_cpu(trx.magic) == TRX_MAGIC) { ++ if (le32_to_cpu(trx.flag_version >> 16) == 2) { ++ pr_notice("dual image TRX header found\n"); ++ return mtd->size / 2; ++ } else { ++ return 0; ++ } ++ } ++ } ++ return 0; ++} ++ ++ ++static int ++find_root(struct mtd_info *mtd, struct mtd_partition *part) ++{ ++ struct trx_header trx, *trx2; ++ unsigned char buf[512], *block; ++ int off, blocksize, trxoff = 0; ++ u32 i, crc = ~0; ++ size_t len; ++ bool edimax = false; ++ ++ blocksize = mtd->erasesize; ++ if (blocksize < 0x10000) ++ blocksize = 0x10000; ++ ++ for (off = (128*1024); off < mtd->size; off += blocksize) { ++ memset(&trx, 0xe5, sizeof(trx)); ++ ++ /* ++ * Read into buffer ++ */ ++ if (mtd->read(mtd, off, sizeof(trx), &len, (char *) &trx) || ++ len != sizeof(trx)) ++ continue; ++ ++ /* found an edimax header */ ++ if (le32_to_cpu(trx.magic) == EDIMAX_PS_HEADER_MAGIC) { ++ /* read the correct trx header */ ++ if (mtd->read(mtd, off + EDIMAX_PS_HEADER_LEN, ++ sizeof(trx), &len, (char *) &trx) || ++ len != sizeof(trx)) { ++ continue; ++ } else { ++ pr_notice("Found an edimax ps header\n"); ++ edimax = true; ++ } ++ } ++ ++ /* found a TRX header */ ++ if (le32_to_cpu(trx.magic) == TRX_MAGIC) { ++ part->offset = le32_to_cpu(trx.offsets[2]) ? : ++ le32_to_cpu(trx.offsets[1]); ++ part->size = le32_to_cpu(trx.len); ++ ++ part->size -= part->offset; ++ part->offset += off; ++ if (edimax) { ++ off += EDIMAX_PS_HEADER_LEN; ++ trxoff = EDIMAX_PS_HEADER_LEN; ++ } ++ ++ goto found; ++ } ++ } ++ ++ pr_warn("%s: Couldn't find root filesystem\n", ++ mtd->name); ++ return -1; ++ ++ found: ++ pr_notice("TRX offset : %x\n", trxoff); ++ if (part->size == 0) ++ return 0; ++ ++ if (mtd->read(mtd, part->offset, sizeof(buf), &len, buf) || len != sizeof(buf)) ++ return 0; ++ ++ /* Move the fs outside of the trx */ ++ part->size = 0; ++ ++ if (trx.len != part->offset + part->size - off) { ++ /* Update the trx offsets and length */ ++ trx.len = part->offset + part->size - off; ++ ++ /* Update the trx crc32 */ ++ for (i = (u32) &(((struct trx_header *)NULL)->flag_version); i <= trx.len; i += sizeof(buf)) { ++ if (mtd->read(mtd, off + i, sizeof(buf), &len, buf) || len != sizeof(buf)) ++ return 0; ++ crc = crc32_le(crc, buf, min(sizeof(buf), trx.len - i)); ++ } ++ trx.crc32 = crc; ++ ++ /* read first eraseblock from the trx */ ++ block = kmalloc(mtd->erasesize, GFP_KERNEL); ++ trx2 = (struct trx_header *) block; ++ if (mtd->read(mtd, off - trxoff, mtd->erasesize, &len, block) || len != mtd->erasesize) { ++ pr_err("Error accessing the first trx eraseblock\n"); ++ return 0; ++ } ++ ++ pr_notice("Updating TRX offsets and length:\n"); ++ pr_notice("old trx = [0x%08x, 0x%08x, 0x%08x], len=0x%08x crc32=0x%08x\n", trx2->offsets[0], trx2->offsets[1], trx2->offsets[2], trx2->len, trx2->crc32); ++ pr_notice("new trx = [0x%08x, 0x%08x, 0x%08x], len=0x%08x crc32=0x%08x\n", trx.offsets[0], trx.offsets[1], trx.offsets[2], trx.len, trx.crc32); ++ ++ /* Write updated trx header to the flash */ ++ memcpy(block + trxoff, &trx, sizeof(trx)); ++ if (mtd->unlock) ++ mtd->unlock(mtd, off - trxoff, mtd->erasesize); ++ erase_write(mtd, off - trxoff, mtd->erasesize, block); ++ if (mtd->sync) ++ mtd->sync(mtd); ++ kfree(block); ++ pr_notice("Done\n"); ++ } ++ ++ return part->size; ++} ++ ++static int get_router(void) ++{ ++ char buf[20]; ++ u32 boardnum = 0; ++ u16 boardtype = 0; ++ u16 boardrev = 0; ++ u32 boardflags = 0; ++ u16 sdram_init = 0; ++ u16 cardbus = 0; ++ u16 strev = 0; ++ ++ if (nvram_getenv("boardnum", buf, sizeof(buf)) >= 0) ++ boardnum = simple_strtoul(buf, NULL, 0); ++ if (nvram_getenv("boardtype", buf, sizeof(buf)) >= 0) ++ boardtype = simple_strtoul(buf, NULL, 0); ++ if (nvram_getenv("boardrev", buf, sizeof(buf)) >= 0) ++ boardrev = simple_strtoul(buf, NULL, 0); ++ if (nvram_getenv("boardflags", buf, sizeof(buf)) >= 0) ++ boardflags = simple_strtoul(buf, NULL, 0); ++ if (nvram_getenv("sdram_init", buf, sizeof(buf)) >= 0) ++ sdram_init = simple_strtoul(buf, NULL, 0); ++ if (nvram_getenv("cardbus", buf, sizeof(buf)) >= 0) ++ cardbus = simple_strtoul(buf, NULL, 0); ++ if (nvram_getenv("st_rev", buf, sizeof(buf)) >= 0) ++ strev = simple_strtoul(buf, NULL, 0); ++ ++ if ((boardnum == 8 || boardnum == 01) ++ && boardtype == 0x0472 && cardbus == 1) { ++ /* Netgear WNR834B, Netgear WNR834Bv2 */ ++ return ROUTER_NETGEAR_WNR834B; ++ } ++ ++ if (boardnum == 01 && boardtype == 0x0472 && boardrev == 0x23) { ++ /* Netgear WNDR-3300 */ ++ return ROUTER_NETGEAR_WNDR3300; ++ } ++ ++ if ((boardnum == 83258 || boardnum == 01) ++ && boardtype == 0x048e ++ && (boardrev == 0x11 || boardrev == 0x10) ++ && boardflags == 0x750 ++ && sdram_init == 0x000A) { ++ /* Netgear WGR614v8/L/WW 16MB ram, cfe v1.3 or v1.5 */ ++ return ROUTER_NETGEAR_WGR614L; ++ } ++ ++ if ((boardnum == 1 || boardnum == 3500) ++ && boardtype == 0x04CF ++ && (boardrev == 0x1213 || boardrev == 02)) { ++ /* Netgear WNR3500v2/U/L */ ++ return ROUTER_NETGEAR_WNR3500L; ++ } ++ ++ if (boardnum == 1 && boardtype == 0xb4cf && boardrev == 0x1100) { ++ /* Netgear WNDR3400 */ ++ return ROUTER_NETGEAR_WNDR3400; ++ } ++ ++ if (boardtype == 0x042f ++ && boardrev == 0x10 ++ && boardflags == 0 ++ && strev == 0x11) { ++ /* Simpletech Simpleshare */ ++ return ROUTER_SIMPLETECH_SIMPLESHARE; ++ } ++ ++ return 0; ++} ++ ++static int parse_bcm47xx_partitions(struct mtd_info *mtd, ++ struct mtd_partition **pparts, ++ struct mtd_part_parser_data *data) ++{ ++ int cfe_size; ++ int dual_image_offset = 0; ++ /* e.g Netgear 0x003e0000-0x003f0000 : "board_data", we exclude this ++ * part from our mapping to prevent overwriting len/checksum on e.g. ++ * Netgear WGR614v8/L/WW ++ */ ++ int custom_data_size = 0; ++ struct mtd_partition *bcm47xx_parts; ++ ++ cfe_size = find_cfe_size(mtd); ++ if (cfe_size < 0) ++ return 0; ++ ++ bcm47xx_parts = kzalloc(sizeof(struct mtd_partition) * 6, GFP_KERNEL); ++ ++ bcm47xx_parts[0].name = "cfe"; ++ bcm47xx_parts[1].name = "linux"; ++ bcm47xx_parts[2].name = "rootfs"; ++ bcm47xx_parts[3].name = "nvram"; ++ ++ /* boot loader */ ++ bcm47xx_parts[0].mask_flags = MTD_WRITEABLE; ++ bcm47xx_parts[0].offset = 0; ++ bcm47xx_parts[0].size = cfe_size; ++ ++ /* nvram */ ++ if (cfe_size != 384 * 1024) { ++ ++ switch (get_router()) { ++ case ROUTER_NETGEAR_WGR614L: ++ case ROUTER_NETGEAR_WNR834B: ++ case ROUTER_NETGEAR_WNDR3300: ++ case ROUTER_NETGEAR_WNR3500L: ++ case ROUTER_NETGEAR_WNDR3400: ++ /* Netgear: checksum is @ 0x003AFFF8 for 4M flash or checksum ++ * is @ 0x007AFFF8 for 8M flash ++ */ ++ custom_data_size = mtd->erasesize; ++ ++ bcm47xx_parts[3].offset = mtd->size - roundup(NVRAM_SPACE, mtd->erasesize); ++ bcm47xx_parts[3].size = roundup(NVRAM_SPACE, mtd->erasesize); ++ ++ /* Place CFE board_data into a partition */ ++ bcm47xx_parts[4].name = "board_data"; ++ bcm47xx_parts[4].offset = bcm47xx_parts[3].offset - custom_data_size; ++ bcm47xx_parts[4].size = custom_data_size; ++ break; ++ ++ case ROUTER_SIMPLETECH_SIMPLESHARE: ++ /* Fixup Simpletech Simple share nvram */ ++ ++ pr_notice("Setting up simpletech nvram\n"); ++ custom_data_size = mtd->erasesize; ++ ++ bcm47xx_parts[3].offset = mtd->size - roundup(NVRAM_SPACE, mtd->erasesize) * 2; ++ bcm47xx_parts[3].size = roundup(NVRAM_SPACE, mtd->erasesize); ++ ++ /* Place backup nvram into a partition */ ++ bcm47xx_parts[4].name = "nvram_copy"; ++ bcm47xx_parts[4].offset = mtd->size - roundup(NVRAM_SPACE, mtd->erasesize); ++ bcm47xx_parts[4].size = roundup(NVRAM_SPACE, mtd->erasesize); ++ break; ++ ++ default: ++ bcm47xx_parts[3].offset = mtd->size - roundup(NVRAM_SPACE, mtd->erasesize); ++ bcm47xx_parts[3].size = roundup(NVRAM_SPACE, mtd->erasesize); ++ } ++ ++ } else { ++ /* nvram (old 128kb config partition on netgear wgt634u) */ ++ bcm47xx_parts[3].offset = bcm47xx_parts[0].size; ++ bcm47xx_parts[3].size = roundup(NVRAM_SPACE, mtd->erasesize); ++ } ++ ++ /* dual image offset*/ ++ pr_notice("Looking for dual image\n"); ++ dual_image_offset = find_dual_image_off(mtd); ++ /* linux (kernel and rootfs) */ ++ if (cfe_size != 384 * 1024) { ++ if (get_router() == ROUTER_SIMPLETECH_SIMPLESHARE) { ++ bcm47xx_parts[1].offset = bcm47xx_parts[0].size; ++ bcm47xx_parts[1].size = bcm47xx_parts[4].offset - dual_image_offset - ++ bcm47xx_parts[1].offset - custom_data_size; ++ } else { ++ bcm47xx_parts[1].offset = bcm47xx_parts[0].size; ++ bcm47xx_parts[1].size = bcm47xx_parts[3].offset - dual_image_offset - ++ bcm47xx_parts[1].offset - custom_data_size; ++ } ++ } else { ++ /* do not count the elf loader, which is on one block */ ++ bcm47xx_parts[1].offset = bcm47xx_parts[0].size + ++ bcm47xx_parts[3].size + mtd->erasesize; ++ bcm47xx_parts[1].size = mtd->size - ++ bcm47xx_parts[0].size - ++ (2*bcm47xx_parts[3].size) - ++ mtd->erasesize - custom_data_size; ++ } ++ ++ /* find and size rootfs */ ++ find_root(mtd, &bcm47xx_parts[2]); ++ bcm47xx_parts[2].size = mtd->size - dual_image_offset - ++ bcm47xx_parts[2].offset - ++ bcm47xx_parts[3].size - custom_data_size; ++ *pparts = bcm47xx_parts; ++ return bcm47xx_parts[4].name == NULL ? 4 : 5; ++} ++ ++static struct mtd_part_parser bcm47xx_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = parse_bcm47xx_partitions, ++ .name = "bcm47xx", ++}; ++ ++static int __init bcm47xx_parser_init(void) ++{ ++ return register_mtd_parser(&bcm47xx_parser); ++} ++ ++static void __exit bcm47xx_parser_exit(void) ++{ ++ deregister_mtd_parser(&bcm47xx_parser); ++} ++ ++module_init(bcm47xx_parser_init); ++module_exit(bcm47xx_parser_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Parsing code for flash partitions on bcm47xx SoCs"); diff --git a/target/linux/brcm47xx/patches-3.3/026-mtd-bcm47xx-add-parallel-flash-driver.patch b/target/linux/brcm47xx/patches-3.3/026-mtd-bcm47xx-add-parallel-flash-driver.patch new file mode 100644 index 000000000..7f8f64f28 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/026-mtd-bcm47xx-add-parallel-flash-driver.patch @@ -0,0 +1,216 @@ +--- a/drivers/mtd/maps/Kconfig ++++ b/drivers/mtd/maps/Kconfig +@@ -248,6 +248,15 @@ config MTD_LANTIQ + help + Support for NOR flash attached to the Lantiq SoC's External Bus Unit. + ++config MTD_BCM47XX_PFLASH ++ tristate "bcm47xx parallel flash support" ++ default y ++ depends on BCM47XX ++ select MTD_PARTITIONS ++ select MTD_BCM47XX_PARTS ++ help ++ Support for bcm47xx parallel flash ++ + config MTD_DILNETPC + tristate "CFI Flash device mapped on DIL/Net PC" + depends on X86 && MTD_CFI_INTELEXT && BROKEN +--- a/drivers/mtd/maps/Makefile ++++ b/drivers/mtd/maps/Makefile +@@ -57,3 +57,4 @@ obj-$(CONFIG_MTD_VMU) += vmu-flash.o + obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o + obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o + obj-$(CONFIG_MTD_LANTIQ) += lantiq-flash.o ++obj-$(CONFIG_MTD_BCM47XX_PFLASH)+= bcm47xx-pflash.o +--- /dev/null ++++ b/drivers/mtd/maps/bcm47xx-pflash.c +@@ -0,0 +1,188 @@ ++/* ++ * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> ++ * Copyright (C) 2005 Waldemar Brodkorb <wbx@openwrt.org> ++ * Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org) ++ * ++ * original functions for finding root filesystem from Mike Baker ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN ++ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ++ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ++ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Copyright 2001-2003, Broadcom Corporation ++ * All Rights Reserved. ++ * ++ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY ++ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM ++ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS ++ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. ++ * ++ * Flash mapping for BCM947XX boards ++ */ ++ ++#define pr_fmt(fmt) "bcm47xx_pflash: " fmt ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/partitions.h> ++#include <linux/io.h> ++#include <asm/mach-bcm47xx/bcm47xx.h> ++#include <linux/platform_device.h> ++ ++#define WINDOW_ADDR 0x1fc00000 ++#define WINDOW_SIZE 0x400000 ++#define BUSWIDTH 2 ++ ++static struct mtd_info *bcm47xx_mtd; ++ ++static void bcm47xx_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) ++{ ++ if (len == 1) { ++ memcpy_fromio(to, map->virt + from, len); ++ } else { ++ int i; ++ u16 *dest = (u16 *) to; ++ u16 *src = (u16 *) (map->virt + from); ++ for (i = 0; i < (len / 2); i++) ++ dest[i] = src[i]; ++ if (len & 1) ++ *((u8 *)dest+len-1) = src[i] & 0xff; ++ } ++} ++ ++static struct map_info bcm47xx_map = { ++ name: "Physically mapped flash", ++ size : WINDOW_SIZE, ++ bankwidth : BUSWIDTH, ++ phys : WINDOW_ADDR, ++}; ++ ++static const char *probes[] = { "bcm47xx", NULL }; ++ ++static int bcm47xx_mtd_probe(struct platform_device *pdev) ++{ ++#ifdef CONFIG_BCM47XX_SSB ++ struct ssb_chipcommon *ssb_cc; ++#endif ++#ifdef CONFIG_BCM47XX_BCMA ++ struct bcma_drv_cc *bcma_cc; ++#endif ++ int ret = 0; ++ ++ switch (bcm47xx_bus_type) { ++#ifdef CONFIG_BCM47XX_SSB ++ case BCM47XX_BUS_TYPE_SSB: ++ ssb_cc = &bcm47xx_bus.ssb.chipco; ++ if (ssb_cc->flash_type != SSB_PFLASH) ++ return -ENODEV; ++ ++ bcm47xx_map.phys = ssb_cc->pflash.window; ++ bcm47xx_map.size = ssb_cc->pflash.window_size; ++ bcm47xx_map.bankwidth = ssb_cc->pflash.buswidth; ++ break; ++#endif ++#ifdef CONFIG_BCM47XX_BCMA ++ case BCM47XX_BUS_TYPE_BCMA: ++ bcma_cc = &bcm47xx_bus.bcma.bus.drv_cc; ++ if (bcma_cc->flash_type != BCMA_PFLASH) ++ return -ENODEV; ++ ++ bcm47xx_map.phys = bcma_cc->pflash.window; ++ bcm47xx_map.size = bcma_cc->pflash.window_size; ++ bcm47xx_map.bankwidth = bcma_cc->pflash.buswidth; ++ break; ++#endif ++ } ++ ++ pr_notice("flash init: 0x%08x 0x%08lx\n", bcm47xx_map.phys, bcm47xx_map.size); ++ bcm47xx_map.virt = ioremap_nocache(bcm47xx_map.phys, bcm47xx_map.size); ++ ++ if (!bcm47xx_map.virt) { ++ pr_err("Failed to ioremap\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&bcm47xx_map); ++ /* override copy_from routine */ ++ bcm47xx_map.copy_from = bcm47xx_map_copy_from; ++ ++ bcm47xx_mtd = do_map_probe("cfi_probe", &bcm47xx_map); ++ if (!bcm47xx_mtd) { ++ pr_err("Failed to do_map_probe\n"); ++ ret = -ENXIO; ++ goto err_unmap; ++ } ++ bcm47xx_mtd->owner = THIS_MODULE; ++ ++ pr_notice("Flash device: 0x%lx at 0x%x\n", bcm47xx_map.size, WINDOW_ADDR); ++ ++ ret = mtd_device_parse_register(bcm47xx_mtd, probes, NULL, NULL, 0); ++ ++ if (ret) { ++ pr_err("Flash: mtd_device_register failed\n"); ++ goto err_destroy; ++ } ++ return 0; ++ ++err_destroy: ++ map_destroy(bcm47xx_mtd); ++err_unmap: ++ iounmap(bcm47xx_map.virt); ++ return ret; ++} ++ ++static int __devexit bcm47xx_mtd_remove(struct platform_device *pdev) ++{ ++ mtd_device_unregister(bcm47xx_mtd); ++ map_destroy(bcm47xx_mtd); ++ iounmap(bcm47xx_map.virt); ++ return 0; ++} ++ ++static struct platform_driver bcm47xx_mtd_driver = { ++ .remove = __devexit_p(bcm47xx_mtd_remove), ++ .driver = { ++ .name = "bcm47xx_pflash", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init init_bcm47xx_mtd(void) ++{ ++ int ret = platform_driver_probe(&bcm47xx_mtd_driver, bcm47xx_mtd_probe); ++ ++ if (ret) ++ pr_err("error registering platform driver: %i\n", ret); ++ return ret; ++} ++ ++static void __exit exit_bcm47xx_mtd(void) ++{ ++ platform_driver_unregister(&bcm47xx_mtd_driver); ++} ++ ++module_init(init_bcm47xx_mtd); ++module_exit(exit_bcm47xx_mtd); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("BCM47XX parallel flash driver"); diff --git a/target/linux/brcm47xx/patches-3.3/027-mtd-bcm47xx-add-serial-flash-driver.patch b/target/linux/brcm47xx/patches-3.3/027-mtd-bcm47xx-add-serial-flash-driver.patch new file mode 100644 index 000000000..3aec60c11 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/027-mtd-bcm47xx-add-serial-flash-driver.patch @@ -0,0 +1,291 @@ +--- a/drivers/mtd/maps/Kconfig ++++ b/drivers/mtd/maps/Kconfig +@@ -257,6 +257,15 @@ config MTD_BCM47XX_PFLASH + help + Support for bcm47xx parallel flash + ++config MTD_BCM47XX_SFLASH ++ tristate "bcm47xx serial flash support" ++ default y ++ depends on BCM47XX ++ select MTD_PARTITIONS ++ select MTD_BCM47XX_PARTS ++ help ++ Support for bcm47xx parallel flash ++ + config MTD_DILNETPC + tristate "CFI Flash device mapped on DIL/Net PC" + depends on X86 && MTD_CFI_INTELEXT && BROKEN +--- a/drivers/mtd/maps/Makefile ++++ b/drivers/mtd/maps/Makefile +@@ -58,3 +58,4 @@ obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr + obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o + obj-$(CONFIG_MTD_LANTIQ) += lantiq-flash.o + obj-$(CONFIG_MTD_BCM47XX_PFLASH)+= bcm47xx-pflash.o ++obj-$(CONFIG_MTD_BCM47XX_SFLASH)+= bcm47xx-sflash.o +--- /dev/null ++++ b/drivers/mtd/maps/bcm47xx-sflash.c +@@ -0,0 +1,263 @@ ++/* ++ * Broadcom SiliconBackplane chipcommon serial flash interface ++ * ++ * Copyright 2011, 2012, Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright 2006, Broadcom Corporation ++ * All Rights Reserved. ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++ ++#define pr_fmt(fmt) "bcm47xx_sflash: " fmt ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/ioport.h> ++#include <linux/sched.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/partitions.h> ++#include <linux/errno.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <bcm47xx.h> ++#include <bus.h> ++ ++static int ++sflash_mtd_poll(struct bcm47xx_sflash *sflash, unsigned int offset, int timeout) ++{ ++ unsigned long now = jiffies; ++ ++ for (;;) { ++ if (!sflash->poll(sflash, offset)) { ++ break; ++ } ++ if (time_after(jiffies, now + timeout)) { ++ pr_err("timeout while polling\n"); ++ return -ETIMEDOUT; ++ ++ } ++ cpu_relax(); ++ udelay(1); ++ } ++ ++ return 0; ++} ++ ++static int ++sflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) ++{ ++ struct bcm47xx_sflash *sflash = (struct bcm47xx_sflash *)mtd->priv; ++ ++ /* Check address range */ ++ if (!len) ++ return 0; ++ ++ if ((from + len) > mtd->size) ++ return -EINVAL; ++ ++ *retlen = 0; ++ while (len) { ++ int ret = sflash->read(sflash, from, len, buf); ++ if (ret < 0) ++ return ret; ++ ++ from += (loff_t) ret; ++ len -= ret; ++ buf += ret; ++ *retlen += ret; ++ } ++ ++ return 0; ++} ++ ++static int ++sflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) ++{ ++ int bytes; ++ int ret; ++ struct bcm47xx_sflash *sflash = (struct bcm47xx_sflash *)mtd->priv; ++ ++ /* Check address range */ ++ if (!len) ++ return 0; ++ ++ if ((to + len) > mtd->size) ++ return -EINVAL; ++ ++ *retlen = 0; ++ while (len) { ++ ret = sflash->write(sflash, to, len, buf); ++ if (ret < 0) ++ return ret; ++ ++ bytes = ret; ++ ++ ret = sflash_mtd_poll(sflash, (unsigned int) to, HZ / 10); ++ if (ret) ++ return ret; ++ ++ to += (loff_t) bytes; ++ len -= bytes; ++ buf += bytes; ++ *retlen += bytes; ++ } ++ ++ return 0; ++} ++ ++static int ++sflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) ++{ ++ struct bcm47xx_sflash *sflash = (struct bcm47xx_sflash *) mtd->priv; ++ int i, j, ret = 0; ++ unsigned int addr, len; ++ ++ /* Check address range */ ++ if (!erase->len) ++ return 0; ++ if ((erase->addr + erase->len) > mtd->size) ++ return -EINVAL; ++ ++ addr = erase->addr; ++ len = erase->len; ++ ++ /* Ensure that requested regions are aligned */ ++ for (i = 0; i < mtd->numeraseregions; i++) { ++ for (j = 0; j < mtd->eraseregions[i].numblocks; j++) { ++ if (addr == mtd->eraseregions[i].offset + ++ mtd->eraseregions[i].erasesize * j && ++ len >= mtd->eraseregions[i].erasesize) { ++ ret = sflash->erase(sflash, addr); ++ if (ret < 0) ++ break; ++ ret = sflash_mtd_poll(sflash, addr, 10 * HZ); ++ if (ret) ++ break; ++ addr += mtd->eraseregions[i].erasesize; ++ len -= mtd->eraseregions[i].erasesize; ++ } ++ } ++ if (ret) ++ break; ++ } ++ ++ /* Set erase status */ ++ if (ret) ++ erase->state = MTD_ERASE_FAILED; ++ else ++ erase->state = MTD_ERASE_DONE; ++ ++ /* Call erase callback */ ++ if (erase->callback) ++ erase->callback(erase); ++ ++ return ret; ++} ++ ++static const char *probes[] = { "bcm47xx", NULL }; ++ ++static int bcm47xx_sflash_probe(struct platform_device *pdev) ++{ ++ struct bcm47xx_sflash *sflash = dev_get_platdata(&pdev->dev); ++ struct mtd_info *mtd; ++ struct mtd_erase_region_info *eraseregions; ++ int ret = 0; ++ ++ mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); ++ if (!mtd){ ++ ret = -ENOMEM; ++ goto err_out; ++ } ++ ++ eraseregions = kzalloc(sizeof(struct mtd_erase_region_info), GFP_KERNEL); ++ if (!eraseregions) { ++ ret = -ENOMEM; ++ goto err_free_mtd; ++ } ++ ++ pr_info("found serial flash: blocksize=%dKB, numblocks=%d, size=%dKB\n", ++ sflash->blocksize / 1024, sflash->numblocks, sflash->size / 1024); ++ ++ /* Setup region info */ ++ eraseregions->offset = 0; ++ eraseregions->erasesize = sflash->blocksize; ++ eraseregions->numblocks = sflash->numblocks; ++ if (eraseregions->erasesize > mtd->erasesize) ++ mtd->erasesize = eraseregions->erasesize; ++ mtd->size = sflash->size; ++ mtd->numeraseregions = 1; ++ ++ /* Register with MTD */ ++ mtd->name = "bcm47xx-sflash"; ++ mtd->type = MTD_NORFLASH; ++ mtd->flags = MTD_CAP_NORFLASH; ++ mtd->eraseregions = eraseregions; ++ mtd->erase = sflash_mtd_erase; ++ mtd->read = sflash_mtd_read; ++ mtd->write = sflash_mtd_write; ++ mtd->writesize = 1; ++ mtd->priv = sflash; ++ ret = dev_set_drvdata(&pdev->dev, mtd); ++ mtd->owner = THIS_MODULE; ++ if (ret) { ++ pr_err("adding private data failed\n"); ++ goto err_free_eraseregions; ++ } ++ ++ ret = mtd_device_parse_register(mtd, probes, NULL, NULL, 0); ++ ++ if (ret) { ++ pr_err("mtd_device_register failed\n"); ++ goto err_free_eraseregions; ++ } ++ return 0; ++ ++err_free_eraseregions: ++ kfree(eraseregions); ++err_free_mtd: ++ kfree(mtd); ++err_out: ++ return ret; ++} ++ ++static int __devexit bcm47xx_sflash_remove(struct platform_device *pdev) ++{ ++ struct mtd_info *mtd = dev_get_drvdata(&pdev->dev); ++ ++ if (mtd) { ++ mtd_device_unregister(mtd); ++ map_destroy(mtd); ++ kfree(mtd->eraseregions); ++ kfree(mtd); ++ dev_set_drvdata(&pdev->dev, NULL); ++ } ++ return 0; ++} ++ ++static struct platform_driver bcm47xx_sflash_driver = { ++ .remove = __devexit_p(bcm47xx_sflash_remove), ++ .driver = { ++ .name = "bcm47xx_sflash", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init init_bcm47xx_sflash(void) ++{ ++ int ret = platform_driver_probe(&bcm47xx_sflash_driver, bcm47xx_sflash_probe); ++ ++ if (ret) ++ pr_err("error registering platform driver: %i\n", ret); ++ return ret; ++} ++ ++static void __exit exit_bcm47xx_sflash(void) ++{ ++ platform_driver_unregister(&bcm47xx_sflash_driver); ++} ++ ++module_init(init_bcm47xx_sflash); ++module_exit(exit_bcm47xx_sflash); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("BCM47XX parallel flash driver"); diff --git a/target/linux/brcm47xx/patches-3.3/028-bcm47xx-register-flash-drivers.patch b/target/linux/brcm47xx/patches-3.3/028-bcm47xx-register-flash-drivers.patch new file mode 100644 index 000000000..7de051dad --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/028-bcm47xx-register-flash-drivers.patch @@ -0,0 +1,136 @@ +--- a/arch/mips/bcm47xx/Kconfig ++++ b/arch/mips/bcm47xx/Kconfig +@@ -9,6 +9,7 @@ config BCM47XX_SSB + select SSB_EMBEDDED + select SSB_B43_PCI_BRIDGE if PCI + select SSB_PCICORE_HOSTMODE if PCI ++ select SSB_SFLASH + default y + help + Add support for old Broadcom BCM47xx boards with Sonics Silicon Backplane support. +@@ -23,6 +24,7 @@ config BCM47XX_BCMA + select BCMA_DRIVER_MIPS + select BCMA_HOST_PCI if PCI + select BCMA_DRIVER_PCI_HOSTMODE if PCI ++ select BCMA_SFLASH + default y + help + Add support for new Broadcom BCM47xx boards with Broadcom specific Advanced Microcontroller Bus. +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -31,10 +31,12 @@ + #include <linux/ssb/ssb.h> + #include <linux/ssb/ssb_embedded.h> + #include <linux/bcma/bcma_soc.h> ++#include <linux/platform_device.h> + #include <asm/bootinfo.h> + #include <asm/reboot.h> + #include <asm/time.h> + #include <bcm47xx.h> ++#include <bus.h> + #include <asm/mach-bcm47xx/nvram.h> + + union bcm47xx_bus bcm47xx_bus; +@@ -45,6 +47,32 @@ EXPORT_SYMBOL(bcm47xx_bus_type); + + struct bcm47xx_sflash bcm47xx_sflash; + ++static struct resource bcm47xx_pflash_resource = { ++ .name = "bcm47xx_pflash", ++ .start = 0, ++ .end = 0, ++ .flags = 0, ++}; ++ ++static struct platform_device bcm47xx_pflash_dev = { ++ .name = "bcm47xx_pflash", ++ .resource = &bcm47xx_pflash_resource, ++ .num_resources = 1, ++}; ++ ++static struct resource bcm47xx_sflash_resource = { ++ .name = "bcm47xx_sflash", ++ .start = 0, ++ .end = 0, ++ .flags = 0, ++}; ++ ++static struct platform_device bcm47xx_sflash_dev = { ++ .name = "bcm47xx_sflash", ++ .resource = &bcm47xx_sflash_resource, ++ .num_resources = 1, ++}; ++ + static void bcm47xx_machine_restart(char *command) + { + printk(KERN_ALERT "Please stand by while rebooting the system...\n"); +@@ -156,6 +184,24 @@ static void __init bcm47xx_register_ssb( + } + } + } ++ ++static int __init bcm47xx_register_flash_ssb(void) ++{ ++ struct ssb_chipcommon *chipco = &bcm47xx_bus.ssb.chipco; ++ ++ switch (chipco->flash_type) { ++ case SSB_PFLASH: ++ bcm47xx_pflash_resource.start = chipco->pflash.window; ++ bcm47xx_pflash_resource.end = chipco->pflash.window + chipco->pflash.window_size; ++ return platform_device_register(&bcm47xx_pflash_dev); ++ case SSB_SFLASH: ++ bcm47xx_sflash_dev.dev.platform_data = &bcm47xx_sflash; ++ return platform_device_register(&bcm47xx_sflash_dev); ++ default: ++ printk(KERN_ERR "No flash device found\n"); ++ return -1; ++ } ++} + #endif + + #ifdef CONFIG_BCM47XX_BCMA +@@ -205,6 +251,24 @@ static void __init bcm47xx_register_bcma + + bcm47xx_fill_bcma_boardinfo(&bcm47xx_bus.bcma.bus.boardinfo, NULL); + } ++ ++static int __init bcm47xx_register_flash_bcma(void) ++{ ++ struct bcma_drv_cc *drv_cc = &bcm47xx_bus.bcma.bus.drv_cc; ++ ++ switch (drv_cc->flash_type) { ++ case BCMA_PFLASH: ++ bcm47xx_pflash_resource.start = drv_cc->pflash.window; ++ bcm47xx_pflash_resource.end = drv_cc->pflash.window + drv_cc->pflash.window_size; ++ return platform_device_register(&bcm47xx_pflash_dev); ++ case BCMA_SFLASH: ++ bcm47xx_sflash_dev.dev.platform_data = &bcm47xx_sflash; ++ return platform_device_register(&bcm47xx_sflash_dev); ++ default: ++ printk(KERN_ERR "No flash device found\n"); ++ return -1; ++ } ++} + #endif + + void __init plat_mem_setup(void) +@@ -247,3 +311,19 @@ static int __init bcm47xx_register_bus_c + return 0; + } + device_initcall(bcm47xx_register_bus_complete); ++ ++static int __init bcm47xx_register_flash(void) ++{ ++ switch (bcm47xx_bus_type) { ++#ifdef CONFIG_BCM47XX_SSB ++ case BCM47XX_BUS_TYPE_SSB: ++ return bcm47xx_register_flash_ssb(); ++#endif ++#ifdef CONFIG_BCM47XX_BCMA ++ case BCM47XX_BUS_TYPE_BCMA: ++ return bcm47xx_register_flash_bcma(); ++#endif ++ } ++ return -1; ++} ++fs_initcall(bcm47xx_register_flash); diff --git a/target/linux/brcm47xx/patches-3.3/029-bcm47xx-read-nvram-from-sflash.patch b/target/linux/brcm47xx/patches-3.3/029-bcm47xx-read-nvram-from-sflash.patch new file mode 100644 index 000000000..1a994d903 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/029-bcm47xx-read-nvram-from-sflash.patch @@ -0,0 +1,143 @@ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -20,11 +20,12 @@ + #include <asm/addrspace.h> + #include <asm/mach-bcm47xx/nvram.h> + #include <asm/mach-bcm47xx/bcm47xx.h> ++#include <asm/mach-bcm47xx/bus.h> + + static char nvram_buf[NVRAM_SPACE]; + + /* Probe for NVRAM header */ +-static void early_nvram_init(void) ++static void early_nvram_init_pflash(void) + { + #ifdef CONFIG_BCM47XX_SSB + struct ssb_chipcommon *ssb_cc; +@@ -50,9 +51,6 @@ static void early_nvram_init(void) + #ifdef CONFIG_BCM47XX_BCMA + case BCM47XX_BUS_TYPE_BCMA: + bcma_cc = &bcm47xx_bus.bcma.bus.drv_cc; +- if (bcma_cc->flash_type != BCMA_PFLASH) +- return; +- + base = bcma_cc->pflash.window; + lim = bcma_cc->pflash.window_size; + break; +@@ -86,7 +84,115 @@ found: + for (i = 0; i < sizeof(struct nvram_header); i += 4) + *dst++ = *src++; + for (; i < header->len && i < NVRAM_SPACE; i += 4) +- *dst++ = le32_to_cpu(*src++); ++ *dst++ = *src++; ++} ++ ++static int find_nvram_size(void) ++{ ++ struct nvram_header header; ++ int nvram_sizes[] = {NVRAM_SPACE, 0xF000, 2 * NVRAM_SPACE}; ++ int i; ++ int ret; ++ ++ for (i = 0; i < sizeof(nvram_sizes); i++) { ++ ret = bcm47xx_sflash.read(&bcm47xx_sflash, bcm47xx_sflash.size - nvram_sizes[i], sizeof(header), (u8 *)&header); ++ if (ret != sizeof(header)) ++ return ret; ++ if (header.magic == NVRAM_HEADER) ++ return nvram_sizes[i]; ++ } ++ return -1; ++} ++ ++static int early_nvram_init_sflash(void) ++{ ++ u32 off; ++ int ret; ++ char *dst; ++ int len; ++ int size; ++ ++ /* check if the struct is already initilized */ ++ if (!bcm47xx_sflash.size) ++ return -1; ++ ++ size = find_nvram_size(); ++ if (size <= 0) ++ return size; ++ ++ len = NVRAM_SPACE; ++ dst = nvram_buf; ++ off = bcm47xx_sflash.size; ++ if (size > len) { ++ printk(KERN_WARNING "nvram on flash is bigger than the reserved" ++ " space in memory, will just copy the first %i bytes\n", ++ len); ++ } ++ while (len) { ++ ret = bcm47xx_sflash.read(&bcm47xx_sflash, off - size, len, dst); ++ if (ret < 0) ++ return ret; ++ off += ret; ++ len -= ret; ++ dst += ret; ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_BCM47XX_SSB ++static void early_nvram_init_ssb(void) ++{ ++ int err; ++ ++ switch (bcm47xx_bus.ssb.chipco.flash_type) { ++ case SSB_PFLASH: ++ early_nvram_init_pflash(); ++ break; ++ case SSB_SFLASH: ++ err = early_nvram_init_sflash(); ++ if (err < 0) ++ printk(KERN_WARNING "can not read from flash: %i\n", err); ++ break; ++ default: ++ printk(KERN_WARNING "unknow flash type\n"); ++ } ++} ++#endif ++ ++#ifdef CONFIG_BCM47XX_BCMA ++static void early_nvram_init_bcma(void) ++{ ++ int err; ++ ++ switch (bcm47xx_bus.bcma.bus.drv_cc.flash_type) { ++ case BCMA_PFLASH: ++ early_nvram_init_pflash(); ++ break; ++ case BCMA_SFLASH: ++ err = early_nvram_init_sflash(); ++ if (err < 0) ++ printk(KERN_WARNING "can not read from flash: %i\n", err); ++ break; ++ default: ++ printk(KERN_WARNING "unknow flash type\n"); ++ } ++} ++#endif ++ ++static void early_nvram_init(void) ++{ ++ switch (bcm47xx_bus_type) { ++#ifdef CONFIG_BCM47XX_SSB ++ case BCM47XX_BUS_TYPE_SSB: ++ early_nvram_init_ssb(); ++ break; ++#endif ++#ifdef CONFIG_BCM47XX_BCMA ++ case BCM47XX_BUS_TYPE_BCMA: ++ early_nvram_init_bcma(); ++ break; ++#endif ++ } + } + + int nvram_getenv(char *name, char *val, size_t val_len) diff --git a/target/linux/brcm47xx/patches-3.3/030-bcm47xx-bcma-nandflash.patch b/target/linux/brcm47xx/patches-3.3/030-bcm47xx-bcma-nandflash.patch new file mode 100644 index 000000000..53f6e98d1 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/030-bcm47xx-bcma-nandflash.patch @@ -0,0 +1,1146 @@ +--- a/arch/mips/bcm47xx/Kconfig ++++ b/arch/mips/bcm47xx/Kconfig +@@ -25,6 +25,7 @@ config BCM47XX_BCMA + select BCMA_HOST_PCI if PCI + select BCMA_DRIVER_PCI_HOSTMODE if PCI + select BCMA_SFLASH ++ select BCMA_NFLASH + default y + help + Add support for new Broadcom BCM47xx boards with Broadcom specific Advanced Microcontroller Bus. +--- a/arch/mips/bcm47xx/bus.c ++++ b/arch/mips/bcm47xx/bus.c +@@ -2,6 +2,7 @@ + * BCM947xx nvram variable access + * + * Copyright (C) 2011 Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright (C) 2011-2012 Tathagata Das <tathagata@alumnux.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the +@@ -46,6 +47,12 @@ void bcm47xx_sflash_struct_bcma_init(str + sflash->numblocks = bcc->sflash.numblocks; + sflash->size = bcc->sflash.size; + } ++ ++void bcm47xx_nflash_struct_bcma_init(struct bcm47xx_nflash *nflash, struct bcma_drv_cc *bcc) ++{ ++ nflash->nflash_type = BCM47XX_BUS_TYPE_BCMA; ++ nflash->bcc = bcc; ++} + #endif + + #ifdef CONFIG_BCM47XX_SSB +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -4,6 +4,7 @@ + * Copyright (C) 2005 Broadcom Corporation + * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2010-2011 Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright (C) 2011-2012 Tathagata Das <tathagata@alumnux.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the +@@ -21,6 +22,7 @@ + #include <asm/mach-bcm47xx/nvram.h> + #include <asm/mach-bcm47xx/bcm47xx.h> + #include <asm/mach-bcm47xx/bus.h> ++#include <linux/mtd/bcm47xx_nand.h> + + static char nvram_buf[NVRAM_SPACE]; + +@@ -160,6 +162,51 @@ static void early_nvram_init_ssb(void) + #endif + + #ifdef CONFIG_BCM47XX_BCMA ++static int early_nvram_init_nflash(void) ++{ ++ struct nvram_header *header; ++ u32 off; ++ int ret; ++ int len; ++ u32 flash_size = bcm47xx_nflash.size; ++ u8 tmpbuf[NFL_SECTOR_SIZE]; ++ int i; ++ u32 *src, *dst; ++ ++ /* check if the struct is already initilized */ ++ if (!flash_size) ++ return -1; ++ ++ cfe_env = 0; ++ ++ off = FLASH_MIN; ++ while (off <= flash_size) { ++ ret = bcma_nflash_read(bcm47xx_nflash.bcc, off, NFL_SECTOR_SIZE, tmpbuf); ++ if (ret != NFL_SECTOR_SIZE) ++ goto done; ++ header = (struct nvram_header *)tmpbuf; ++ if (header->magic == NVRAM_HEADER) ++ goto found; ++ off <<= 1; ++ } ++ ++ ret = -1; ++ goto done; ++ ++found: ++ len = header->len; ++ header = (struct nvram_header *) KSEG1ADDR(NAND_FLASH1 + off); ++ src = (u32 *) header; ++ dst = (u32 *) nvram_buf; ++ for (i = 0; i < sizeof(struct nvram_header); i += 4) ++ *dst++ = *src++; ++ for (; i < len && i < NVRAM_SPACE; i += 4) ++ *dst++ = *src++; ++ ret = 0; ++done: ++ return ret; ++} ++ + static void early_nvram_init_bcma(void) + { + int err; +@@ -173,6 +220,11 @@ static void early_nvram_init_bcma(void) + if (err < 0) + printk(KERN_WARNING "can not read from flash: %i\n", err); + break; ++ case BCMA_NFLASH: ++ err = early_nvram_init_nflash(); ++ if (err < 0) ++ printk(KERN_WARNING "can not read from nflash: %i\n", err); ++ break; + default: + printk(KERN_WARNING "unknow flash type\n"); + } +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -4,6 +4,7 @@ + * Copyright (C) 2006 Michael Buesch <m@bues.ch> + * Copyright (C) 2010 Waldemar Brodkorb <wbx@openadk.org> + * Copyright (C) 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright (C) 2011-2012 Tathagata Das <tathagata@alumnux.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the +@@ -234,6 +235,21 @@ static int bcm47xx_get_sprom_bcma(struct + } + } + ++struct bcm47xx_nflash bcm47xx_nflash; ++ ++static struct resource bcm47xx_nflash_resource = { ++ .name = "bcm47xx_nflash", ++ .start = 0, ++ .end = 0, ++ .flags = 0, ++}; ++ ++static struct platform_device bcm47xx_nflash_dev = { ++ .name = "bcm47xx_nflash", ++ .resource = &bcm47xx_nflash_resource, ++ .num_resources = 1, ++}; ++ + static void __init bcm47xx_register_bcma(void) + { + int err; +@@ -248,6 +264,9 @@ static void __init bcm47xx_register_bcma + + if (bcm47xx_bus.bcma.bus.drv_cc.flash_type == BCMA_SFLASH) + bcm47xx_sflash_struct_bcma_init(&bcm47xx_sflash, &bcm47xx_bus.bcma.bus.drv_cc); ++ ++ if (bcm47xx_bus.bcma.bus.drv_cc.flash_type == BCMA_NFLASH) ++ bcm47xx_nflash_struct_bcma_init(&bcm47xx_nflash, &bcm47xx_bus.bcma.bus.drv_cc); + + bcm47xx_fill_bcma_boardinfo(&bcm47xx_bus.bcma.bus.boardinfo, NULL); + } +@@ -264,6 +283,9 @@ static int __init bcm47xx_register_flash + case BCMA_SFLASH: + bcm47xx_sflash_dev.dev.platform_data = &bcm47xx_sflash; + return platform_device_register(&bcm47xx_sflash_dev); ++ case BCMA_NFLASH: ++ bcm47xx_nflash_dev.dev.platform_data = &bcm47xx_nflash; ++ return platform_device_register(&bcm47xx_nflash_dev); + default: + printk(KERN_ERR "No flash device found\n"); + return -1; +--- a/arch/mips/include/asm/mach-bcm47xx/bus.h ++++ b/arch/mips/include/asm/mach-bcm47xx/bus.h +@@ -2,6 +2,7 @@ + * BCM947xx nvram variable access + * + * Copyright (C) 2011 Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright (C) 2011-2012 Tathagata Das <tathagata@alumnux.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the +@@ -12,6 +13,7 @@ + #include <linux/ssb/ssb.h> + #include <linux/bcma/bcma.h> + #include <bcm47xx.h> ++#include <linux/mtd/nand.h> + + struct bcm47xx_sflash { + enum bcm47xx_bus_type sflash_type; +@@ -34,3 +36,18 @@ void bcm47xx_sflash_struct_bcma_init(str + void bcm47xx_sflash_struct_ssb_init(struct bcm47xx_sflash *sflash, struct ssb_chipcommon *scc); + + extern struct bcm47xx_sflash bcm47xx_sflash; ++ ++struct bcm47xx_nflash { ++ enum bcm47xx_bus_type nflash_type; ++ struct bcma_drv_cc *bcc; ++ ++ u32 size; /* Total size in bytes */ ++ u32 next_opcode; /* Next expected command from upper NAND layer */ ++ ++ struct mtd_info mtd; ++ struct nand_chip nand; ++}; ++ ++void bcm47xx_nflash_struct_bcma_init(struct bcm47xx_nflash *nflash, struct bcma_drv_cc *bcc); ++ ++extern struct bcm47xx_nflash bcm47xx_nflash; +--- a/drivers/bcma/Kconfig ++++ b/drivers/bcma/Kconfig +@@ -43,6 +43,11 @@ config BCMA_SFLASH + depends on BCMA_DRIVER_MIPS + default y + ++config BCMA_NFLASH ++ bool ++ depends on BCMA_DRIVER_MIPS ++ default y ++ + config BCMA_DRIVER_MIPS + bool "BCMA Broadcom MIPS core driver" + depends on BCMA && MIPS +--- a/drivers/bcma/Makefile ++++ b/drivers/bcma/Makefile +@@ -1,6 +1,7 @@ + bcma-y += main.o scan.o core.o sprom.o + bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o + bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o ++bcma-$(CONFIG_BCMA_NFLASH) += driver_chipcommon_nflash.o + bcma-y += driver_pci.o + bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o + bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o +--- a/drivers/bcma/bcma_private.h ++++ b/drivers/bcma/bcma_private.h +@@ -56,6 +56,11 @@ u32 bcma_pmu_get_clockcpu(struct bcma_dr + int bcma_sflash_init(struct bcma_drv_cc *cc); + #endif /* CONFIG_BCMA_SFLASH */ + ++#ifdef CONFIG_BCMA_NFLASH ++/* driver_chipcommon_nflash.c */ ++int bcma_nflash_init(struct bcma_drv_cc *cc); ++#endif /* CONFIG_BCMA_NFLASH */ ++ + #ifdef CONFIG_BCMA_HOST_PCI + /* host_pci.c */ + extern int __init bcma_host_pci_init(void); +--- /dev/null ++++ b/drivers/bcma/driver_chipcommon_nflash.c +@@ -0,0 +1,154 @@ ++/* ++ * BCMA nand flash interface ++ * ++ * Copyright 2011, Tathagata Das <tathagata@alumnux.com> ++ * Copyright 2010, Broadcom Corporation ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++ ++#include <linux/bcma/bcma.h> ++#include <linux/bcma/bcma_driver_chipcommon.h> ++#include <linux/delay.h> ++#include <linux/mtd/bcm47xx_nand.h> ++#include <linux/mtd/nand.h> ++ ++#include "bcma_private.h" ++ ++/* Issue a nand flash command */ ++static inline void bcma_nflash_cmd(struct bcma_drv_cc *cc, u32 opcode) ++{ ++ bcma_cc_write32(cc, NAND_CMD_START, opcode); ++ bcma_cc_read32(cc, NAND_CMD_START); ++} ++ ++/* Check offset and length */ ++static int bcma_nflash_offset_is_valid(struct bcma_drv_cc *cc, u32 offset, u32 len, u32 mask) ++{ ++ if ((offset & mask) != 0 || (len & mask) != 0) { ++ pr_err("%s(): Address is not aligned. offset: %x, len: %x, mask: %x\n", __func__, offset, len, mask); ++ return 1; ++ } ++ ++ if ((((offset + len) >> 20) >= cc->nflash.size) && ++ (((offset + len) & ((1 << 20) - 1)) != 0)) { ++ pr_err("%s(): Address is outside Flash memory region. offset: %x, len: %x, mask: %x\n", __func__, offset, len, mask); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* Read len bytes starting at offset into buf. Returns number of bytes read. */ ++int bcma_nflash_read(struct bcma_drv_cc *cc, u32 offset, u32 len, u8 *buf) ++{ ++ u32 mask; ++ int i; ++ u32 *to, val, res; ++ ++ mask = NFL_SECTOR_SIZE - 1; ++ if (bcma_nflash_offset_is_valid(cc, offset, len, mask)) ++ return 0; ++ ++ to = (u32 *)buf; ++ res = len; ++ while (res > 0) { ++ bcma_cc_write32(cc, NAND_CMD_ADDR, offset); ++ bcma_nflash_cmd(cc, NCMD_PAGE_RD); ++ if (bcma_nflash_poll(cc) < 0) ++ break; ++ val = bcma_cc_read32(cc, NAND_INTFC_STATUS); ++ if ((val & NIST_CACHE_VALID) == 0) ++ break; ++ bcma_cc_write32(cc, NAND_CACHE_ADDR, 0); ++ for (i = 0; i < NFL_SECTOR_SIZE; i += 4, to++) { ++ *to = bcma_cc_read32(cc, NAND_CACHE_DATA); ++ } ++ res -= NFL_SECTOR_SIZE; ++ offset += NFL_SECTOR_SIZE; ++ } ++ return (len - res); ++} ++ ++#define NF_RETRIES 1000000 ++ ++/* Poll for command completion. Returns zero when complete. */ ++int bcma_nflash_poll(struct bcma_drv_cc *cc) ++{ ++ u32 retries = NF_RETRIES; ++ u32 pollmask = NIST_CTRL_READY|NIST_FLASH_READY; ++ u32 mask; ++ ++ while (retries--) { ++ mask = bcma_cc_read32(cc, NAND_INTFC_STATUS) & pollmask; ++ if (mask == pollmask) ++ return 0; ++ cpu_relax(); ++ } ++ ++ if (!retries) { ++ pr_err("bcma_nflash_poll: not ready\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* Write len bytes starting at offset into buf. Returns success (0) or failure (!0). ++ * Should poll for completion. ++ */ ++int bcma_nflash_write(struct bcma_drv_cc *cc, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ u32 mask; ++ int i; ++ u32 *from, res, reg; ++ ++ mask = cc->nflash.pagesize - 1; ++ if (bcma_nflash_offset_is_valid(cc, offset, len, mask)) ++ return 1; ++ ++ /* disable partial page enable */ ++ reg = bcma_cc_read32(cc, NAND_ACC_CONTROL); ++ reg &= ~NAC_PARTIAL_PAGE_EN; ++ bcma_cc_write32(cc, NAND_ACC_CONTROL, reg); ++ ++ from = (u32 *)buf; ++ res = len; ++ while (res > 0) { ++ bcma_cc_write32(cc, NAND_CACHE_ADDR, 0); ++ for (i = 0; i < cc->nflash.pagesize; i += 4, from++) { ++ if (i % 512 == 0) ++ bcma_cc_write32(cc, NAND_CMD_ADDR, i); ++ bcma_cc_write32(cc, NAND_CACHE_DATA, *from); ++ } ++ bcma_cc_write32(cc, NAND_CMD_ADDR, offset + cc->nflash.pagesize - 512); ++ bcma_nflash_cmd(cc, NCMD_PAGE_PROG); ++ if (bcma_nflash_poll(cc) < 0) ++ break; ++ res -= cc->nflash.pagesize; ++ offset += cc->nflash.pagesize; ++ } ++ ++ if (res <= 0) ++ return 0; ++ else ++ return (len - res); ++} ++ ++/* Erase a region. Returns success (0) or failure (-1). ++ * Poll for completion. ++ */ ++int bcma_nflash_erase(struct bcma_drv_cc *cc, u32 offset) ++{ ++ if ((offset >> 20) >= cc->nflash.size) ++ return -1; ++ if ((offset & (cc->nflash.blocksize - 1)) != 0) ++ return -1; ++ ++ bcma_cc_write32(cc, NAND_CMD_ADDR, offset); ++ bcma_nflash_cmd(cc, NCMD_BLOCK_ERASE); ++ if (bcma_nflash_poll(cc) < 0) ++ return -1; ++ return 0; ++} +--- a/drivers/bcma/driver_mips.c ++++ b/drivers/bcma/driver_mips.c +@@ -6,6 +6,7 @@ + * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de> + * Copyright 2010, Bernhard Loos <bernhardloos@googlemail.com> + * Copyright 2011, Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright (C) 2011-2012 Tathagata Das <tathagata@alumnux.com> + * + * Licensed under the GNU/GPL. See COPYING for details. + */ +@@ -182,6 +183,17 @@ static void bcma_core_mips_flash_detect( + { + struct bcma_bus *bus = mcore->core->bus; + ++ if (bus->drv_cc.core->id.rev == 38 ++ && (bus->drv_cc.status & (1 << 4)) != 0) { ++#ifdef CONFIG_BCMA_NFLASH ++ pr_info("found nand flash.\n"); ++ bus->drv_cc.flash_type = BCMA_NFLASH; ++#else ++ pr_info("NAND flash not supported.\n"); ++#endif ++ return; ++ } ++ + switch (bus->drv_cc.capabilities & BCMA_CC_CAP_FLASHT) { + case BCMA_CC_FLASHT_STSER: + case BCMA_CC_FLASHT_ATSER: +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -536,4 +536,12 @@ config MTD_NAND_FSMC + Enables support for NAND Flash chips on the ST Microelectronics + Flexible Static Memory Controller (FSMC) + ++config MTD_NAND_BCM47XX ++ tristate "bcm47xx nand flash support" ++ default y ++ depends on BCM47XX && BCMA_NFLASH ++ select MTD_PARTITIONS ++ help ++ Support for bcm47xx nand flash ++ + endif # MTD_NAND +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -49,5 +49,6 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mp + obj-$(CONFIG_MTD_NAND_RICOH) += r852.o + obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o + obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ ++obj-$(CONFIG_MTD_NAND_BCM47XX) += bcm47xx_nand.o + + nand-objs := nand_base.o nand_bbt.o +--- /dev/null ++++ b/drivers/mtd/nand/bcm47xx_nand.c +@@ -0,0 +1,506 @@ ++/* ++ * BCMA nand flash interface ++ * ++ * Copyright (C) 2011-2012 Tathagata Das <tathagata@alumnux.com> ++ * Copyright 2010, Broadcom Corporation ++ * ++ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY ++ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM ++ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS ++ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. ++ * ++ */ ++ ++#define pr_fmt(fmt) "bcm47xx_nflash: " fmt ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/ioport.h> ++#include <linux/sched.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/partitions.h> ++#include <linux/errno.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <bcm47xx.h> ++#include <bus.h> ++#include <linux/cramfs_fs.h> ++#include <linux/romfs_fs.h> ++#include <linux/magic.h> ++#include <linux/byteorder/generic.h> ++#include <linux/mtd/bcm47xx_nand.h> ++#include <linux/mtd/nand.h> ++ ++static int bcm47xx_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip); ++static int bcm47xx_erase(struct mtd_info *mtd, unsigned int addr, unsigned int len); ++ ++/* Private Global variable */ ++static u32 read_offset = 0; ++static u32 write_offset; ++ ++static int ++nflash_mtd_poll(struct bcm47xx_nflash *nflash, unsigned int offset, int timeout) ++{ ++ unsigned long now = jiffies; ++ int ret = 0; ++ ++ for (;;) { ++ if (!bcma_nflash_poll(nflash->bcc)) { ++ ret = 0; ++ break; ++ } ++ if (time_after(jiffies, now + timeout)) { ++ pr_err("timeout while polling\n"); ++ ret = -ETIMEDOUT; ++ break; ++ } ++ udelay(1); ++ } ++ ++ return ret; ++} ++ ++static int ++bcm47xx_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) ++{ ++ struct nand_chip *nchip = (struct nand_chip *)mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ int bytes, ret = 0; ++ u32 extra = 0; ++ u8 *tmpbuf = NULL; ++ int size; ++ u32 offset, blocksize, mask, off; ++ u32 skip_bytes = 0; ++ int need_copy = 0; ++ u8 *ptr = NULL; ++ ++ /* Check address range */ ++ if (!len) ++ return 0; ++ if ((from + len) > mtd->size) ++ return -EINVAL; ++ offset = from; ++ if ((offset & (NFL_SECTOR_SIZE - 1)) != 0) { ++ extra = offset & (NFL_SECTOR_SIZE - 1); ++ offset -= extra; ++ len += extra; ++ need_copy = 1; ++ } ++ size = (len + (NFL_SECTOR_SIZE - 1)) & ~(NFL_SECTOR_SIZE - 1); ++ if (size != len) { ++ need_copy = 1; ++ } ++ if (!need_copy) { ++ ptr = buf; ++ } else { ++ tmpbuf = (u8 *)kmalloc(size, GFP_KERNEL); ++ ptr = tmpbuf; ++ } ++ ++ blocksize = mtd->erasesize; ++ mask = blocksize - 1; ++ *retlen = 0; ++ while (len > 0) { ++ off = offset + skip_bytes; ++ if ((bytes = bcma_nflash_read(nflash->bcc, off, NFL_SECTOR_SIZE, ptr)) < 0) { ++ ret = bytes; ++ goto done; ++ } ++ if (bytes > len) ++ bytes = len; ++ offset += bytes; ++ len -= bytes; ++ ptr += bytes; ++ *retlen += bytes; ++ } ++ ++done: ++ if (tmpbuf) { ++ *retlen -= extra; ++ memcpy(buf, tmpbuf+extra, *retlen); ++ kfree(tmpbuf); ++ } ++ ++ return ret; ++} ++ ++static void bcm47xx_write(struct mtd_info *mtd, u32 to, const u_char *buf, u32 len) ++{ ++ struct nand_chip *nchip = (struct nand_chip *)mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ u32 offset, blocksize, mask, off; ++ int read_len; ++ u32 copy_len, write_len, from; ++ u_char *write_ptr, *block; ++ const u_char *ptr; ++ int ret, bytes; ++ ++ /* Check address range */ ++ if (!len) { ++ pr_err("Error: Attempted to write too small data\n"); ++ return; ++ } ++ ++ if (!to) ++ return; ++ ++ if ((to + len) > mtd->size) { ++ pr_err("Error: Attempted to write too large data\n"); ++ return; ++ } ++ ++ ptr = buf; ++ block = NULL; ++ offset = to; ++ blocksize = mtd->erasesize; ++ if (!(block = kmalloc(blocksize, GFP_KERNEL))) ++ return; ++ mask = blocksize - 1; ++ while (len) { ++ /* Align offset */ ++ from = offset & ~mask; ++ /* Copy existing data into holding block if necessary */ ++ if (((offset & (blocksize-1)) != 0) || (len < blocksize)) { ++ if ((ret = bcm47xx_read(mtd, from, blocksize, &read_len, block))) ++ goto done; ++ if (read_len != blocksize) { ++ ret = -EINVAL; ++ goto done; ++ } ++ } ++ ++ /* Copy input data into holding block */ ++ copy_len = min(len, blocksize - (offset & mask)); ++ memcpy(block + (offset & mask), ptr, copy_len); ++ off = (uint) from; ++ /* Erase block */ ++ if ((ret = bcm47xx_erase(mtd, off, blocksize)) < 0) ++ goto done; ++ /* Write holding block */ ++ write_ptr = block; ++ write_len = blocksize; ++ if ((bytes = bcma_nflash_write(nflash->bcc, (uint)from, (uint)write_len, (u8 *) write_ptr)) != 0) { ++ ret = bytes; ++ goto done; ++ } ++ offset += copy_len; ++ if (len < copy_len) ++ len = 0; ++ else ++ len -= copy_len; ++ ptr += copy_len; ++ } ++ ++done: ++ if (block) ++ kfree(block); ++ return; ++} ++ ++static int bcm47xx_erase(struct mtd_info *mtd, unsigned int addr, unsigned int len) ++{ ++ struct nand_chip *nchip = (struct nand_chip *)mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ ++ /* Check address range */ ++ if (!len) ++ return 1; ++ if ((addr + len) > mtd->size) ++ return 1; ++ ++ if (bcma_nflash_erase(nflash->bcc, addr)) { ++ pr_err("ERASE: nflash erase error\n"); ++ return 1; ++ } ++ ++ if (nflash_mtd_poll(nflash, addr, 10 * HZ)) { ++ pr_err("ERASE: nflash_mtd_poll error\n"); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* This functions is used by upper layer to checks if device is ready */ ++static int bcm47xx_dev_ready(struct mtd_info *mtd) ++{ ++ return 1; ++} ++ ++/* Issue a nand flash command */ ++static inline void bcm47xx_nflash_cmd(struct bcma_drv_cc *cc, u32 opcode) ++{ ++ bcma_cc_write32(cc, NAND_CMD_START, opcode); ++ bcma_cc_read32(cc, NAND_CMD_START); ++} ++ ++static void bcm47xx_command(struct mtd_info *mtd, unsigned command, ++ int column, int page_addr) ++{ ++ struct nand_chip *nchip = (struct nand_chip *)mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ u32 pagesize = 1 << nchip->page_shift; ++ ++ /* Command pre-processing step */ ++ switch (command) { ++ case NAND_CMD_RESET: ++ bcm47xx_nflash_cmd(nflash->bcc, NCMD_FLASH_RESET); ++ break; ++ ++ case NAND_CMD_STATUS: ++ nflash->next_opcode = NAND_CMD_STATUS; ++ read_offset = 0; ++ write_offset = 0; ++ break; ++ ++ case NAND_CMD_READ0: ++ read_offset = page_addr * pagesize; ++ nflash->next_opcode = 0; ++ break; ++ ++ case NAND_CMD_READOOB: ++ read_offset = page_addr * pagesize; ++ nflash->next_opcode = 0; ++ break; ++ ++ case NAND_CMD_SEQIN: ++ write_offset = page_addr * pagesize; ++ nflash->next_opcode = 0; ++ break; ++ ++ case NAND_CMD_PAGEPROG: ++ nflash->next_opcode = 0; ++ break; ++ ++ case NAND_CMD_READID: ++ read_offset = column; ++ bcm47xx_nflash_cmd(nflash->bcc, NCMD_ID_RD); ++ nflash->next_opcode = NAND_DEVID; ++ break; ++ ++ case NAND_CMD_ERASE1: ++ nflash->next_opcode = 0; ++ bcm47xx_erase(mtd, page_addr*pagesize, pagesize); ++ break; ++ ++ case NAND_CMD_ERASE2: ++ break; ++ ++ case NAND_CMD_RNDOUT: ++ if (column > mtd->writesize) ++ read_offset += (column - mtd->writesize); ++ else ++ read_offset += column; ++ break; ++ ++ default: ++ pr_err("COMMAND not supported %x\n", command); ++ nflash->next_opcode = 0; ++ break; ++ } ++} ++ ++/* This function is used by upper layer for select and ++ * deselect of the NAND chip. ++ * It is dummy function. */ ++static void bcm47xx_select_chip(struct mtd_info *mtd, int chip) ++{ ++} ++ ++static u_char bcm47xx_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *nchip = mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ uint8_t ret = 0; ++ static u32 id; ++ ++ if (nflash->next_opcode == 0) ++ return ret; ++ ++ if (nflash->next_opcode == NAND_CMD_STATUS) ++ return NAND_STATUS_WP; ++ ++ id = bcma_cc_read32(nflash->bcc, nflash->next_opcode); ++ ++ if (nflash->next_opcode == NAND_DEVID) { ++ ret = (id >> (8*read_offset)) & 0xff; ++ read_offset++; ++ } ++ ++ return ret; ++} ++ ++static uint16_t bcm47xx_read_word(struct mtd_info *mtd) ++{ ++ loff_t from = read_offset; ++ uint16_t buf = 0; ++ int bytes; ++ ++ bcm47xx_read(mtd, from, sizeof(buf), &bytes, (u_char *)&buf); ++ return buf; ++} ++ ++/* Write data of length len to buffer buf. The data to be ++ * written on NAND Flash is first copied to RAMbuffer. After the Data Input ++ * Operation by the NFC, the data is written to NAND Flash */ ++static void bcm47xx_write_buf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ bcm47xx_write(mtd, write_offset, buf, len); ++} ++ ++/* Read the data buffer from the NAND Flash. To read the data from NAND ++ * Flash first the data output cycle is initiated by the NFC, which copies ++ * the data to RAMbuffer. This data of length len is then copied to buffer buf. ++ */ ++static void bcm47xx_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ loff_t from = read_offset; ++ int bytes; ++ ++ bcm47xx_read(mtd, from, len, &bytes, buf); ++} ++ ++/* Used by the upper layer to verify the data in NAND Flash ++ * with the data in the buf. */ ++static int bcm47xx_verify_buf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ return -EFAULT; ++} ++ ++static int bcm47xx_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) ++{ ++ struct nand_chip *nchip = mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ int i; ++ uint off; ++ u32 pagesize = 1 << nchip->page_shift; ++ u32 blocksize = mtd->erasesize; ++ ++ if ((ofs >> 20) >= nflash->size) ++ return 1; ++ if ((ofs & (blocksize - 1)) != 0) ++ return 1; ++ ++ for (i = 0; i < 2; i++) { ++ off = ofs + pagesize; ++ bcma_cc_write32(nflash->bcc, NAND_CMD_ADDR, off); ++ bcm47xx_nflash_cmd(nflash->bcc, NCMD_SPARE_RD); ++ if (bcma_nflash_poll(nflash->bcc) < 0) ++ break; ++ if ((bcma_cc_read32(nflash->bcc, NAND_INTFC_STATUS) & NIST_SPARE_VALID) != NIST_SPARE_VALID) ++ return 1; ++ if ((bcma_cc_read32(nflash->bcc, NAND_SPARE_RD0) & 0xff) != 0xff) ++ return 1; ++ } ++ return 0; ++} ++ ++const char *part_probes[] = { "cmdlinepart", NULL }; ++static int bcm47xx_probe(struct platform_device *pdev) ++{ ++ struct nand_chip *nchip; ++ struct mtd_info *mtd; ++ struct bcm47xx_nflash *nflash = dev_get_platdata(&pdev->dev); ++ int ret = 0; ++ ++ mtd = &nflash->mtd; ++ nchip = &nflash->nand; ++ ++ /* Register with MTD */ ++ mtd->priv = nchip; ++ mtd->owner = THIS_MODULE; ++ mtd->dev.parent = &pdev->dev; ++ ++ /* 50 us command delay time */ ++ nchip->chip_delay = 50; ++ ++ nchip->priv = nflash; ++ nchip->dev_ready = bcm47xx_dev_ready; ++ nchip->cmdfunc = bcm47xx_command; ++ nchip->select_chip = bcm47xx_select_chip; ++ nchip->read_byte = bcm47xx_read_byte; ++ nchip->read_word = bcm47xx_read_word; ++ nchip->write_buf = bcm47xx_write_buf; ++ nchip->read_buf = bcm47xx_read_buf; ++ nchip->verify_buf = bcm47xx_verify_buf; ++ nchip->block_bad = bcm47xx_block_bad; ++ nchip->options = NAND_SKIP_BBTSCAN; ++ ++ /* Not known */ ++ nchip->ecc.mode = NAND_ECC_NONE; ++ ++ /* first scan to find the device and get the page size */ ++ if (nand_scan_ident(mtd, 1, NULL)) { ++ pr_err("nand_scan_ident failed\n"); ++ ret = -ENXIO; ++ goto done; ++ } ++ nflash->bcc->nflash.size = mtd->size; ++ nflash->bcc->nflash.pagesize = 1 << nchip->page_shift; ++ nflash->bcc->nflash.blocksize = mtd->erasesize; ++ bcm47xx_nflash.size = mtd->size; ++ ++ /* second phase scan */ ++ if (nand_scan_tail(mtd)) { ++ pr_err("nand_scan_tail failed\n"); ++ ret = -ENXIO; ++ goto done; ++ } ++ ++ mtd->name = "bcm47xx-nflash"; ++ mtd->flags |= MTD_WRITEABLE; ++ ret = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); ++ ++ if (ret) { ++ pr_err("mtd_device_register failed\n"); ++ return ret; ++ } ++ ++ return 0; ++ ++done: ++ return ret; ++} ++ ++static int __devexit bcm47xx_remove(struct platform_device *pdev) ++{ ++ struct bcm47xx_nflash *nflash = dev_get_platdata(&pdev->dev); ++ struct mtd_info *mtd = &nflash->mtd; ++ ++ if (nflash) { ++ /* Release resources, unregister device */ ++ nand_release(mtd); ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver bcm47xx_driver = { ++ .remove = __devexit_p(bcm47xx_remove), ++ .driver = { ++ .name = "bcm47xx_nflash", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init init_bcm47xx_nflash(void) ++{ ++ int ret = platform_driver_probe(&bcm47xx_driver, bcm47xx_probe); ++ ++ if (ret) ++ pr_err("error registering platform driver: %i\n", ret); ++ return ret; ++} ++ ++static void __exit exit_bcm47xx_nflash(void) ++{ ++ platform_driver_unregister(&bcm47xx_driver); ++} ++ ++module_init(init_bcm47xx_nflash); ++module_exit(exit_bcm47xx_nflash); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("BCM47XX NAND flash driver"); +--- a/include/linux/bcma/bcma_driver_chipcommon.h ++++ b/include/linux/bcma/bcma_driver_chipcommon.h +@@ -436,6 +436,7 @@ struct bcma_chipcommon_pmu { + enum bcma_flash_type { + BCMA_PFLASH, + BCMA_SFLASH, ++ BCMA_NFLASH, + }; + + struct bcma_pflash { +@@ -452,6 +453,14 @@ struct bcma_sflash { + }; + #endif /* CONFIG_BCMA_SFLASH */ + ++#ifdef CONFIG_BCMA_NFLASH ++struct bcma_nflash { ++ u32 blocksize; /* Block size */ ++ u32 pagesize; /* Page size */ ++ u32 size; /* Total size in bytes */ ++}; ++#endif ++ + struct bcma_serial_port { + void *regs; + unsigned long clockspeed; +@@ -477,6 +486,9 @@ struct bcma_drv_cc { + #ifdef CONFIG_BCMA_SFLASH + struct bcma_sflash sflash; + #endif /* CONFIG_BCMA_SFLASH */ ++#ifdef CONFIG_BCMA_NFLASH ++ struct bcma_nflash nflash; ++#endif + }; + + int nr_serial_ports; +@@ -542,4 +554,13 @@ int bcma_sflash_write(struct bcma_drv_cc + int bcma_sflash_erase(struct bcma_drv_cc *cc, u32 offset); + #endif /* CONFIG_BCMA_SFLASH */ + ++#ifdef CONFIG_BCMA_NFLASH ++/* Chipcommon nflash support. */ ++int bcma_nflash_read(struct bcma_drv_cc *cc, u32 offset, u32 len, u8 *buf); ++int bcma_nflash_poll(struct bcma_drv_cc *cc); ++int bcma_nflash_write(struct bcma_drv_cc *cc, u32 offset, u32 len, const u8 *buf); ++int bcma_nflash_erase(struct bcma_drv_cc *cc, u32 offset); ++int bcma_nflash_commit(struct bcma_drv_cc *cc, u32 offset, u32 len, const u8 *buf); ++#endif ++ + #endif /* LINUX_BCMA_DRIVER_CC_H_ */ +--- /dev/null ++++ b/include/linux/mtd/bcm47xx_nand.h +@@ -0,0 +1,134 @@ ++/* ++ * Broadcom chipcommon NAND flash interface ++ * ++ * Copyright (C) 2011-2012 Tathagata Das <tathagata@alumnux.com> ++ * Copyright (C) 2009, Broadcom Corporation ++ * All Rights Reserved. ++ * ++ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY ++ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM ++ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS ++ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. ++ * ++ */ ++ ++#ifndef _nflash_h_ ++#define _nflash_h_ ++ ++#define NAND_FLASH1 0x1fc00000 /* MIPS Flash Region 1 */ ++ ++/* nand_cmd_start commands */ ++#define NCMD_NULL 0 ++#define NCMD_PAGE_RD 1 ++#define NCMD_SPARE_RD 2 ++#define NCMD_STATUS_RD 3 ++#define NCMD_PAGE_PROG 4 ++#define NCMD_SPARE_PROG 5 ++#define NCMD_COPY_BACK 6 ++#define NCMD_ID_RD 7 ++#define NCMD_BLOCK_ERASE 8 ++#define NCMD_FLASH_RESET 9 ++#define NCMD_LOCK 0xa ++#define NCMD_LOCK_DOWN 0xb ++#define NCMD_UNLOCK 0xc ++#define NCMD_LOCK_STATUS 0xd ++ ++/* nand_acc_control */ ++#define NAC_RD_ECC_EN 0x80000000 ++#define NAC_WR_ECC_EN 0x40000000 ++#define NAC_RD_ECC_BLK0_EN 0x20000000 ++#define NAC_FAST_PGM_RDIN 0x10000000 ++#define NAC_RD_ERASED_ECC_EN 0x08000000 ++#define NAC_PARTIAL_PAGE_EN 0x04000000 ++#define NAC_PAGE_HIT_EN 0x01000000 ++#define NAC_ECC_LEVEL0 0x00f00000 ++#define NAC_ECC_LEVEL 0x000f0000 ++#define NAC_SPARE_SIZE0 0x00003f00 ++#define NAC_SPARE_SIZE 0x0000003f ++ ++/* nand_config */ ++#define NCF_CONFIG_LOCK 0x80000000 ++#define NCF_BLOCK_SIZE_MASK 0x70000000 ++#define NCF_BLOCK_SIZE_SHIFT 28 ++#define NCF_DEVICE_SIZE_MASK 0x0f000000 ++#define NCF_DEVICE_SIZE_SHIFT 24 ++#define NCF_DEVICE_WIDTH 0x00800000 ++#define NCF_PAGE_SIZE_MASK 0x00300000 ++#define NCF_PAGE_SIZE_SHIFT 20 ++#define NCF_FULL_ADDR_BYTES_MASK 0x00070000 ++#define NCF_FULL_ADDR_BYTES_SHIFT 16 ++#define NCF_COL_ADDR_BYTES_MASK 0x00007000 ++#define NCF_COL_ADDR_BYTES_SHIFT 12 ++#define NCF_BLK_ADDR_BYTES_MASK 0x00000700 ++#define NCF_BLK_ADDR_BYTES_SHIFT 8 ++ ++/* nand_intfc_status */ ++#define NIST_CTRL_READY 0x80000000 ++#define NIST_FLASH_READY 0x40000000 ++#define NIST_CACHE_VALID 0x20000000 ++#define NIST_SPARE_VALID 0x10000000 ++#define NIST_ERASED 0x08000000 ++#define NIST_STATUS 0x000000ff ++ ++#define NFL_SECTOR_SIZE 512 ++ ++#define NFL_TABLE_END 0xffffffff ++#define NFL_BOOT_SIZE 0x200000 ++#define NFL_BOOT_OS_SIZE 0x2000000 ++ ++/* Nand flash MLC controller registers (corerev >= 38) */ ++#define NAND_REVISION 0xC00 ++#define NAND_CMD_START 0xC04 ++#define NAND_CMD_ADDR_X 0xC08 ++#define NAND_CMD_ADDR 0xC0C ++#define NAND_CMD_END_ADDR 0xC10 ++#define NAND_CS_NAND_SELECT 0xC14 ++#define NAND_CS_NAND_XOR 0xC18 ++#define NAND_SPARE_RD0 0xC20 ++#define NAND_SPARE_RD4 0xC24 ++#define NAND_SPARE_RD8 0xC28 ++#define NAND_SPARE_RD12 0xC2C ++#define NAND_SPARE_WR0 0xC30 ++#define NAND_SPARE_WR4 0xC34 ++#define NAND_SPARE_WR8 0xC38 ++#define NAND_SPARE_WR12 0xC3C ++#define NAND_ACC_CONTROL 0xC40 ++#define NAND_CONFIG 0xC48 ++#define NAND_TIMING_1 0xC50 ++#define NAND_TIMING_2 0xC54 ++#define NAND_SEMAPHORE 0xC58 ++#define NAND_DEVID 0xC60 ++#define NAND_DEVID_X 0xC64 ++#define NAND_BLOCK_LOCK_STATUS 0xC68 ++#define NAND_INTFC_STATUS 0xC6C ++#define NAND_ECC_CORR_ADDR_X 0xC70 ++#define NAND_ECC_CORR_ADDR 0xC74 ++#define NAND_ECC_UNC_ADDR_X 0xC78 ++#define NAND_ECC_UNC_ADDR 0xC7C ++#define NAND_READ_ERROR_COUNT 0xC80 ++#define NAND_CORR_STAT_THRESHOLD 0xC84 ++#define NAND_READ_ADDR_X 0xC90 ++#define NAND_READ_ADDR 0xC94 ++#define NAND_PAGE_PROGRAM_ADDR_X 0xC98 ++#define NAND_PAGE_PROGRAM_ADDR 0xC9C ++#define NAND_COPY_BACK_ADDR_X 0xCA0 ++#define NAND_COPY_BACK_ADDR 0xCA4 ++#define NAND_BLOCK_ERASE_ADDR_X 0xCA8 ++#define NAND_BLOCK_ERASE_ADDR 0xCAC ++#define NAND_INV_READ_ADDR_X 0xCB0 ++#define NAND_INV_READ_ADDR 0xCB4 ++#define NAND_BLK_WR_PROTECT 0xCC0 ++#define NAND_ACC_CONTROL_CS1 0xCD0 ++#define NAND_CONFIG_CS1 0xCD4 ++#define NAND_TIMING_1_CS1 0xCD8 ++#define NAND_TIMING_2_CS1 0xCDC ++#define NAND_SPARE_RD16 0xD30 ++#define NAND_SPARE_RD20 0xD34 ++#define NAND_SPARE_RD24 0xD38 ++#define NAND_SPARE_RD28 0xD3C ++#define NAND_CACHE_ADDR 0xD40 ++#define NAND_CACHE_DATA 0xD44 ++#define NAND_CTRL_CONFIG 0xD48 ++#define NAND_CTRL_STATUS 0xD4C ++ ++#endif /* _nflash_h_ */ diff --git a/target/linux/brcm47xx/patches-3.3/044-bcma-add-PCIe-host-controller.patch b/target/linux/brcm47xx/patches-3.3/044-bcma-add-PCIe-host-controller.patch new file mode 100644 index 000000000..e5740693f --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/044-bcma-add-PCIe-host-controller.patch @@ -0,0 +1,76 @@ +--- a/arch/mips/pci/pci-bcm47xx.c ++++ b/arch/mips/pci/pci-bcm47xx.c +@@ -25,6 +25,7 @@ + #include <linux/types.h> + #include <linux/pci.h> + #include <linux/ssb/ssb.h> ++#include <linux/bcma/bcma.h> + #include <bcm47xx.h> + + int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +@@ -32,15 +33,12 @@ int __init pcibios_map_irq(const struct + return 0; + } + +-int pcibios_plat_dev_init(struct pci_dev *dev) +-{ + #ifdef CONFIG_BCM47XX_SSB ++static int bcm47xx_pcibios_plat_dev_init_ssb(struct pci_dev *dev) ++{ + int res; + u8 slot, pin; + +- if (bcm47xx_bus_type != BCM47XX_BUS_TYPE_SSB) +- return 0; +- + res = ssb_pcibios_plat_dev_init(dev); + if (res < 0) { + printk(KERN_ALERT "PCI: Failed to init device %s\n", +@@ -60,6 +58,47 @@ int pcibios_plat_dev_init(struct pci_dev + } + + dev->irq = res; ++ return 0; ++} + #endif ++ ++#ifdef CONFIG_BCM47XX_BCMA ++static int bcm47xx_pcibios_plat_dev_init_bcma(struct pci_dev *dev) ++{ ++ int res; ++ ++ res = bcma_core_pci_plat_dev_init(dev); ++ if (res < 0) { ++ printk(KERN_ALERT "PCI: Failed to init device %s\n", ++ pci_name(dev)); ++ return res; ++ } ++ ++ res = bcma_core_pci_pcibios_map_irq(dev); ++ ++ /* IRQ-0 and IRQ-1 are software interrupts. */ ++ if (res < 2) { ++ printk(KERN_ALERT "PCI: Failed to map IRQ of device %s\n", ++ pci_name(dev)); ++ return res; ++ } ++ ++ dev->irq = res; + return 0; + } ++#endif ++ ++int pcibios_plat_dev_init(struct pci_dev *dev) ++{ ++#ifdef CONFIG_BCM47XX_SSB ++ if (bcm47xx_bus_type == BCM47XX_BUS_TYPE_SSB) ++ return bcm47xx_pcibios_plat_dev_init_ssb(dev); ++ else ++#endif ++#ifdef CONFIG_BCM47XX_BCMA ++ if (bcm47xx_bus_type == BCM47XX_BUS_TYPE_BCMA) ++ return bcm47xx_pcibios_plat_dev_init_bcma(dev); ++ else ++#endif ++ return 0; ++} diff --git a/target/linux/brcm47xx/patches-3.3/114-MIPS-BCM47xx-Setup-and-register-serial-early.patch b/target/linux/brcm47xx/patches-3.3/114-MIPS-BCM47xx-Setup-and-register-serial-early.patch new file mode 100644 index 000000000..901397237 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/114-MIPS-BCM47xx-Setup-and-register-serial-early.patch @@ -0,0 +1,69 @@ +From 9be402f069cc259ad5795b77567d66c4e7f6bef6 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens <hauke@hauke-m.de> +Date: Sun, 18 Jul 2010 14:59:24 +0200 +Subject: [PATCH 4/6] MIPS: BCM47xx: Setup and register serial early + +Swap the first and second serial if console=ttyS1 was set. +Set it up and register it for early serial support. + +This patch has been in OpenWRT for a long time. + +Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> +--- + arch/mips/bcm47xx/setup.c | 39 ++++++++++++++++++++++++++++++++++++++- + 1 files changed, 38 insertions(+), 1 deletions(-) + +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -33,6 +33,8 @@ + #include <linux/ssb/ssb_embedded.h> + #include <linux/bcma/bcma_soc.h> + #include <linux/platform_device.h> ++#include <linux/serial.h> ++#include <linux/serial_8250.h> + #include <asm/bootinfo.h> + #include <asm/reboot.h> + #include <asm/time.h> +@@ -152,6 +154,31 @@ static int bcm47xx_get_invariants(struct + return 0; + } + ++#ifdef CONFIG_SERIAL_8250 ++static void __init bcm47xx_early_serial_setup(struct ssb_mipscore *mcore) ++{ ++ int i; ++ ++ for (i = 0; i < mcore->nr_serial_ports; i++) { ++ struct ssb_serial_port *port = &(mcore->serial_ports[i]); ++ struct uart_port s; ++ ++ memset(&s, 0, sizeof(s)); ++ s.line = i; ++ s.mapbase = (unsigned int) port->regs; ++ s.membase = port->regs; ++ s.irq = port->irq + 2; ++ s.uartclk = port->baud_base; ++ s.flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; ++ s.iotype = SERIAL_IO_MEM; ++ s.regshift = port->reg_shift; ++ ++ early_serial_setup(&s); ++ } ++ printk(KERN_DEBUG "Serial init done.\n"); ++} ++#endif ++ + static void __init bcm47xx_register_ssb(void) + { + int err; +@@ -184,6 +211,10 @@ static void __init bcm47xx_register_ssb( + memcpy(&mcore->serial_ports[1], &port, sizeof(port)); + } + } ++ ++#ifdef CONFIG_SERIAL_8250 ++ bcm47xx_early_serial_setup(mcore); ++#endif + } + + static int __init bcm47xx_register_flash_ssb(void) diff --git a/target/linux/brcm47xx/patches-3.3/116-MIPS-BCM47xx-Remove-CFE-console.patch b/target/linux/brcm47xx/patches-3.3/116-MIPS-BCM47xx-Remove-CFE-console.patch new file mode 100644 index 000000000..4a84b6b60 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/116-MIPS-BCM47xx-Remove-CFE-console.patch @@ -0,0 +1,141 @@ +From 5219981646071abb6731634bf47781a53e248764 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens <hauke@hauke-m.de> +Date: Sun, 18 Jul 2010 15:11:26 +0200 +Subject: [PATCH 6/6] MIPS: BCM47xx: Remove CFE console + +Do not use the CFE console. It causes hangs on some devices like the +Buffalo WHR-HP-G54. +This was reported in https://dev.openwrt.org/ticket/4061 and +https://forum.openwrt.org/viewtopic.php?id=17063 + +Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> +--- + arch/mips/Kconfig | 1 - + arch/mips/bcm47xx/prom.c | 82 +++------------------------------------------ + 2 files changed, 6 insertions(+), 77 deletions(-) + +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -100,7 +100,6 @@ config BCM47XX + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + select GENERIC_GPIO +- select SYS_HAS_EARLY_PRINTK + select CFE + help + Support for BCM47XX based boards +--- a/arch/mips/bcm47xx/prom.c ++++ b/arch/mips/bcm47xx/prom.c +@@ -31,96 +31,28 @@ + #include <asm/fw/cfe/cfe_api.h> + #include <asm/fw/cfe/cfe_error.h> + +-static int cfe_cons_handle; +- + const char *get_system_type(void) + { + return "Broadcom BCM47XX"; + } + +-void prom_putchar(char c) +-{ +- while (cfe_write(cfe_cons_handle, &c, 1) == 0) +- ; +-} +- +-static __init void prom_init_cfe(void) ++static __init int prom_init_cfe(void) + { + uint32_t cfe_ept; + uint32_t cfe_handle; + uint32_t cfe_eptseal; +- int argc = fw_arg0; +- char **envp = (char **) fw_arg2; +- int *prom_vec = (int *) fw_arg3; +- +- /* +- * Check if a loader was used; if NOT, the 4 arguments are +- * what CFE gives us (handle, 0, EPT and EPTSEAL) +- */ +- if (argc < 0) { +- cfe_handle = (uint32_t)argc; +- cfe_ept = (uint32_t)envp; +- cfe_eptseal = (uint32_t)prom_vec; +- } else { +- if ((int)prom_vec < 0) { +- /* +- * Old loader; all it gives us is the handle, +- * so use the "known" entrypoint and assume +- * the seal. +- */ +- cfe_handle = (uint32_t)prom_vec; +- cfe_ept = 0xBFC00500; +- cfe_eptseal = CFE_EPTSEAL; +- } else { +- /* +- * Newer loaders bundle the handle/ept/eptseal +- * Note: prom_vec is in the loader's useg +- * which is still alive in the TLB. +- */ +- cfe_handle = prom_vec[0]; +- cfe_ept = prom_vec[2]; +- cfe_eptseal = prom_vec[3]; +- } +- } ++ ++ cfe_eptseal = (uint32_t) fw_arg3; ++ cfe_handle = (uint32_t) fw_arg0; ++ cfe_ept = (uint32_t) fw_arg2; + + if (cfe_eptseal != CFE_EPTSEAL) { +- /* too early for panic to do any good */ + printk(KERN_ERR "CFE's entrypoint seal doesn't match."); +- while (1) ; ++ return -1; + } + + cfe_init(cfe_handle, cfe_ept); +-} +- +-static __init void prom_init_console(void) +-{ +- /* Initialize CFE console */ +- cfe_cons_handle = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE); +-} +- +-static __init void prom_init_cmdline(void) +-{ +- static char buf[COMMAND_LINE_SIZE] __initdata; +- +- /* Get the kernel command line from CFE */ +- if (cfe_getenv("LINUX_CMDLINE", buf, COMMAND_LINE_SIZE) >= 0) { +- buf[COMMAND_LINE_SIZE - 1] = 0; +- strcpy(arcs_cmdline, buf); +- } +- +- /* Force a console handover by adding a console= argument if needed, +- * as CFE is not available anymore later in the boot process. */ +- if ((strstr(arcs_cmdline, "console=")) == NULL) { +- /* Try to read the default serial port used by CFE */ +- if ((cfe_getenv("BOOT_CONSOLE", buf, COMMAND_LINE_SIZE) < 0) +- || (strncmp("uart", buf, 4))) +- /* Default to uart0 */ +- strcpy(buf, "uart0"); +- +- /* Compute the new command line */ +- snprintf(arcs_cmdline, COMMAND_LINE_SIZE, "%s console=ttyS%c,115200", +- arcs_cmdline, buf[4]); +- } ++ return 0; + } + + static __init void prom_init_mem(void) +@@ -161,8 +93,6 @@ static __init void prom_init_mem(void) + void __init prom_init(void) + { + prom_init_cfe(); +- prom_init_console(); +- prom_init_cmdline(); + prom_init_mem(); + } + diff --git a/target/linux/brcm47xx/patches-3.3/119-fix-boot.patch b/target/linux/brcm47xx/patches-3.3/119-fix-boot.patch new file mode 100644 index 000000000..5eb8b08bf --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/119-fix-boot.patch @@ -0,0 +1,16 @@ +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -92,11 +92,13 @@ config ATH79 + + config BCM47XX + bool "Broadcom BCM47XX based boards" ++ select BOOT_RAW + select CEVT_R4K + select CSRC_R4K + select DMA_NONCOHERENT + select HW_HAS_PCI + select IRQ_CPU ++ select NO_EXCEPT_FILL + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + select GENERIC_GPIO diff --git a/target/linux/brcm47xx/patches-3.3/150-cpu_fixes.patch b/target/linux/brcm47xx/patches-3.3/150-cpu_fixes.patch new file mode 100644 index 000000000..a367525ea --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/150-cpu_fixes.patch @@ -0,0 +1,367 @@ +--- a/arch/mips/include/asm/r4kcache.h ++++ b/arch/mips/include/asm/r4kcache.h +@@ -17,6 +17,20 @@ + #include <asm/cpu-features.h> + #include <asm/mipsmtregs.h> + ++#ifdef CONFIG_BCM47XX ++#include <asm/paccess.h> ++#include <linux/ssb/ssb.h> ++#define BCM4710_DUMMY_RREG() ((void) *((u8 *) KSEG1ADDR(SSB_ENUM_BASE))) ++ ++#define BCM4710_FILL_TLB(addr) (*(volatile unsigned long *)(addr)) ++#define BCM4710_PROTECTED_FILL_TLB(addr) ({ unsigned long x; get_dbe(x, (volatile unsigned long *)(addr)); }) ++#else ++#define BCM4710_DUMMY_RREG() ++ ++#define BCM4710_FILL_TLB(addr) ++#define BCM4710_PROTECTED_FILL_TLB(addr) ++#endif ++ + /* + * This macro return a properly sign-extended address suitable as base address + * for indexed cache operations. Two issues here: +@@ -150,6 +164,7 @@ static inline void flush_icache_line_ind + static inline void flush_dcache_line_indexed(unsigned long addr) + { + __dflush_prologue ++ BCM4710_DUMMY_RREG(); + cache_op(Index_Writeback_Inv_D, addr); + __dflush_epilogue + } +@@ -169,6 +184,7 @@ static inline void flush_icache_line(uns + static inline void flush_dcache_line(unsigned long addr) + { + __dflush_prologue ++ BCM4710_DUMMY_RREG(); + cache_op(Hit_Writeback_Inv_D, addr); + __dflush_epilogue + } +@@ -176,6 +192,7 @@ static inline void flush_dcache_line(uns + static inline void invalidate_dcache_line(unsigned long addr) + { + __dflush_prologue ++ BCM4710_DUMMY_RREG(); + cache_op(Hit_Invalidate_D, addr); + __dflush_epilogue + } +@@ -208,6 +225,7 @@ static inline void flush_scache_line(uns + */ + static inline void protected_flush_icache_line(unsigned long addr) + { ++ BCM4710_DUMMY_RREG(); + protected_cache_op(Hit_Invalidate_I, addr); + } + +@@ -219,6 +237,7 @@ static inline void protected_flush_icach + */ + static inline void protected_writeback_dcache_line(unsigned long addr) + { ++ BCM4710_DUMMY_RREG(); + protected_cache_op(Hit_Writeback_Inv_D, addr); + } + +@@ -339,8 +358,52 @@ static inline void invalidate_tcache_pag + : "r" (base), \ + "i" (op)); + ++static inline void blast_dcache(void) ++{ ++ unsigned long start = KSEG0; ++ unsigned long dcache_size = current_cpu_data.dcache.waysize * current_cpu_data.dcache.ways; ++ unsigned long end = (start + dcache_size); ++ ++ do { ++ BCM4710_DUMMY_RREG(); ++ cache_op(Index_Writeback_Inv_D, start); ++ start += current_cpu_data.dcache.linesz; ++ } while(start < end); ++} ++ ++static inline void blast_dcache_page(unsigned long page) ++{ ++ unsigned long start = page; ++ unsigned long end = start + PAGE_SIZE; ++ ++ BCM4710_FILL_TLB(start); ++ do { ++ BCM4710_DUMMY_RREG(); ++ cache_op(Hit_Writeback_Inv_D, start); ++ start += current_cpu_data.dcache.linesz; ++ } while(start < end); ++} ++ ++static inline void blast_dcache_page_indexed(unsigned long page) ++{ ++ unsigned long start = page; ++ unsigned long end = start + PAGE_SIZE; ++ unsigned long ws_inc = 1UL << current_cpu_data.dcache.waybit; ++ unsigned long ws_end = current_cpu_data.dcache.ways << ++ current_cpu_data.dcache.waybit; ++ unsigned long ws, addr; ++ for (ws = 0; ws < ws_end; ws += ws_inc) { ++ start = page + ws; ++ for (addr = start; addr < end; addr += current_cpu_data.dcache.linesz) { ++ BCM4710_DUMMY_RREG(); ++ cache_op(Index_Writeback_Inv_D, addr); ++ } ++ } ++} ++ ++ + /* build blast_xxx, blast_xxx_page, blast_xxx_page_indexed */ +-#define __BUILD_BLAST_CACHE(pfx, desc, indexop, hitop, lsize) \ ++#define __BUILD_BLAST_CACHE(pfx, desc, indexop, hitop, lsize, war) \ + static inline void blast_##pfx##cache##lsize(void) \ + { \ + unsigned long start = INDEX_BASE; \ +@@ -352,6 +415,7 @@ static inline void blast_##pfx##cache##l + \ + __##pfx##flush_prologue \ + \ ++ war \ + for (ws = 0; ws < ws_end; ws += ws_inc) \ + for (addr = start; addr < end; addr += lsize * 32) \ + cache##lsize##_unroll32(addr|ws, indexop); \ +@@ -366,6 +430,7 @@ static inline void blast_##pfx##cache##l + \ + __##pfx##flush_prologue \ + \ ++ war \ + do { \ + cache##lsize##_unroll32(start, hitop); \ + start += lsize * 32; \ +@@ -384,6 +449,8 @@ static inline void blast_##pfx##cache##l + current_cpu_data.desc.waybit; \ + unsigned long ws, addr; \ + \ ++ war \ ++ \ + __##pfx##flush_prologue \ + \ + for (ws = 0; ws < ws_end; ws += ws_inc) \ +@@ -393,36 +460,38 @@ static inline void blast_##pfx##cache##l + __##pfx##flush_epilogue \ + } + +-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 16) +-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 16) +-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 16) +-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 32) +-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 32) +-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 32) +-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 64) +-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64) +-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 64) +-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 128) +- +-__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 16) +-__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 32) +-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 16) +-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 32) +-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 64) +-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 128) ++__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 16, ) ++__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 16, BCM4710_FILL_TLB(start);) ++__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 16, ) ++__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 32, ) ++__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 32, BCM4710_FILL_TLB(start);) ++__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 32, ) ++__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 64, ) ++__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64, BCM4710_FILL_TLB(start);) ++__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 64, ) ++__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 128, ) ++ ++__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 16, ) ++__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 32, ) ++__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 16, ) ++__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 32, ) ++__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 64, ) ++__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 128, ) + + /* build blast_xxx_range, protected_blast_xxx_range */ +-#define __BUILD_BLAST_CACHE_RANGE(pfx, desc, hitop, prot) \ ++#define __BUILD_BLAST_CACHE_RANGE(pfx, desc, hitop, prot, war, war2) \ + static inline void prot##blast_##pfx##cache##_range(unsigned long start, \ + unsigned long end) \ + { \ + unsigned long lsize = cpu_##desc##_line_size(); \ + unsigned long addr = start & ~(lsize - 1); \ + unsigned long aend = (end - 1) & ~(lsize - 1); \ ++ war \ + \ + __##pfx##flush_prologue \ + \ + while (1) { \ ++ war2 \ + prot##cache_op(hitop, addr); \ + if (addr == aend) \ + break; \ +@@ -432,13 +501,13 @@ static inline void prot##blast_##pfx##ca + __##pfx##flush_epilogue \ + } + +-__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, protected_) +-__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, protected_) +-__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, protected_) +-__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, ) +-__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, ) ++__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, protected_, BCM4710_PROTECTED_FILL_TLB(addr); BCM4710_PROTECTED_FILL_TLB(aend);, BCM4710_DUMMY_RREG();) ++__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, protected_,, ) ++__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, protected_,, ) ++__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D,, BCM4710_FILL_TLB(addr); BCM4710_FILL_TLB(aend);, BCM4710_DUMMY_RREG();) ++__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD,,, ) + /* blast_inv_dcache_range */ +-__BUILD_BLAST_CACHE_RANGE(inv_d, dcache, Hit_Invalidate_D, ) +-__BUILD_BLAST_CACHE_RANGE(inv_s, scache, Hit_Invalidate_SD, ) ++__BUILD_BLAST_CACHE_RANGE(inv_d, dcache, Hit_Invalidate_D,,,BCM4710_DUMMY_RREG();) ++__BUILD_BLAST_CACHE_RANGE(inv_s, scache, Hit_Invalidate_SD,,, ) + + #endif /* _ASM_R4KCACHE_H */ +--- a/arch/mips/include/asm/stackframe.h ++++ b/arch/mips/include/asm/stackframe.h +@@ -449,6 +449,10 @@ + .macro RESTORE_SP_AND_RET + LONG_L sp, PT_R29(sp) + .set mips3 ++#ifdef CONFIG_BCM47XX ++ nop ++ nop ++#endif + eret + .set mips0 + .endm +--- a/arch/mips/kernel/genex.S ++++ b/arch/mips/kernel/genex.S +@@ -52,6 +52,10 @@ NESTED(except_vec1_generic, 0, sp) + NESTED(except_vec3_generic, 0, sp) + .set push + .set noat ++#ifdef CONFIG_BCM47XX ++ nop ++ nop ++#endif + #if R5432_CP0_INTERRUPT_WAR + mfc0 k0, CP0_INDEX + #endif +--- a/arch/mips/mm/c-r4k.c ++++ b/arch/mips/mm/c-r4k.c +@@ -35,6 +35,9 @@ + #include <asm/cacheflush.h> /* for run_uncached() */ + + ++/* For enabling BCM4710 cache workarounds */ ++int bcm4710 = 0; ++ + /* + * Special Variant of smp_call_function for use by cache functions: + * +@@ -111,6 +114,9 @@ static void __cpuinit r4k_blast_dcache_p + { + unsigned long dc_lsize = cpu_dcache_line_size(); + ++ if (bcm4710) ++ r4k_blast_dcache_page = blast_dcache_page; ++ else + if (dc_lsize == 0) + r4k_blast_dcache_page = (void *)cache_noop; + else if (dc_lsize == 16) +@@ -127,6 +133,9 @@ static void __cpuinit r4k_blast_dcache_p + { + unsigned long dc_lsize = cpu_dcache_line_size(); + ++ if (bcm4710) ++ r4k_blast_dcache_page_indexed = blast_dcache_page_indexed; ++ else + if (dc_lsize == 0) + r4k_blast_dcache_page_indexed = (void *)cache_noop; + else if (dc_lsize == 16) +@@ -143,6 +152,9 @@ static void __cpuinit r4k_blast_dcache_s + { + unsigned long dc_lsize = cpu_dcache_line_size(); + ++ if (bcm4710) ++ r4k_blast_dcache = blast_dcache; ++ else + if (dc_lsize == 0) + r4k_blast_dcache = (void *)cache_noop; + else if (dc_lsize == 16) +@@ -683,6 +695,8 @@ static void local_r4k_flush_cache_sigtra + unsigned long addr = (unsigned long) arg; + + R4600_HIT_CACHEOP_WAR_IMPL; ++ BCM4710_PROTECTED_FILL_TLB(addr); ++ BCM4710_PROTECTED_FILL_TLB(addr + 4); + if (dc_lsize) + protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); + if (!cpu_icache_snoops_remote_store && scache_size) +@@ -1349,6 +1363,17 @@ static void __cpuinit coherency_setup(vo + * silly idea of putting something else there ... + */ + switch (current_cpu_type()) { ++ case CPU_BMIPS3300: ++ { ++ u32 cm; ++ cm = read_c0_diag(); ++ /* Enable icache */ ++ cm |= (1 << 31); ++ /* Enable dcache */ ++ cm |= (1 << 30); ++ write_c0_diag(cm); ++ } ++ break; + case CPU_R4000PC: + case CPU_R4000SC: + case CPU_R4000MC: +@@ -1405,6 +1430,15 @@ void __cpuinit r4k_cache_init(void) + break; + } + ++ /* Check if special workarounds are required */ ++#ifdef CONFIG_BCM47XX ++ if (current_cpu_data.cputype == CPU_BMIPS32 && (current_cpu_data.processor_id & 0xff) == 0) { ++ printk("Enabling BCM4710A0 cache workarounds.\n"); ++ bcm4710 = 1; ++ } else ++#endif ++ bcm4710 = 0; ++ + probe_pcache(); + setup_scache(); + +@@ -1465,5 +1499,13 @@ void __cpuinit r4k_cache_init(void) + #if !defined(CONFIG_MIPS_CMP) + local_r4k___flush_cache_all(NULL); + #endif ++#ifdef CONFIG_BCM47XX ++ { ++ static void (*_coherency_setup)(void); ++ _coherency_setup = (void (*)(void)) KSEG1ADDR(coherency_setup); ++ _coherency_setup(); ++ } ++#else + coherency_setup(); ++#endif + } +--- a/arch/mips/mm/tlbex.c ++++ b/arch/mips/mm/tlbex.c +@@ -1264,6 +1264,9 @@ static void __cpuinit build_r4000_tlb_re + /* No need for uasm_i_nop */ + } + ++#ifdef CONFIG_BCM47XX ++ uasm_i_nop(&p); ++#endif + #ifdef CONFIG_64BIT + build_get_pmde64(&p, &l, &r, K0, K1); /* get pmd in K1 */ + #else +@@ -1794,6 +1797,9 @@ build_r4000_tlbchange_handler_head(u32 * + { + struct work_registers wr = build_get_work_registers(p); + ++#ifdef CONFIG_BCM47XX ++ uasm_i_nop(p); ++#endif + #ifdef CONFIG_64BIT + build_get_pmde64(p, l, r, wr.r1, wr.r2); /* get pmd in ptr */ + #else diff --git a/target/linux/brcm47xx/patches-3.3/160-kmap_coherent.patch b/target/linux/brcm47xx/patches-3.3/160-kmap_coherent.patch new file mode 100644 index 000000000..9253e915e --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/160-kmap_coherent.patch @@ -0,0 +1,77 @@ +--- a/arch/mips/include/asm/cpu-features.h ++++ b/arch/mips/include/asm/cpu-features.h +@@ -110,6 +110,9 @@ + #ifndef cpu_has_pindexed_dcache + #define cpu_has_pindexed_dcache (cpu_data[0].dcache.flags & MIPS_CACHE_PINDEX) + #endif ++#ifndef cpu_use_kmap_coherent ++#define cpu_use_kmap_coherent 1 ++#endif + + /* + * I-Cache snoops remote store. This only matters on SMP. Some multiprocessors +--- /dev/null ++++ b/arch/mips/include/asm/mach-bcm47xx/cpu-feature-overrides.h +@@ -0,0 +1,13 @@ ++/* ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org) ++ */ ++#ifndef __ASM_MACH_BCM47XX_CPU_FEATURE_OVERRIDES_H ++#define __ASM_MACH_BCM47XX_CPU_FEATURE_OVERRIDES_H ++ ++#define cpu_use_kmap_coherent 0 ++ ++#endif /* __ASM_MACH_BCM47XX_CPU_FEATURE_OVERRIDES_H */ +--- a/arch/mips/mm/c-r4k.c ++++ b/arch/mips/mm/c-r4k.c +@@ -507,7 +507,7 @@ static inline void local_r4k_flush_cache + */ + map_coherent = (cpu_has_dc_aliases && + page_mapped(page) && !Page_dcache_dirty(page)); +- if (map_coherent) ++ if (map_coherent && cpu_use_kmap_coherent) + vaddr = kmap_coherent(page, addr); + else + vaddr = kmap_atomic(page, KM_USER0); +@@ -530,7 +530,7 @@ static inline void local_r4k_flush_cache + } + + if (vaddr) { +- if (map_coherent) ++ if (map_coherent && cpu_use_kmap_coherent) + kunmap_coherent(); + else + kunmap_atomic(vaddr, KM_USER0); +--- a/arch/mips/mm/init.c ++++ b/arch/mips/mm/init.c +@@ -208,7 +208,7 @@ void copy_user_highpage(struct page *to, + void *vfrom, *vto; + + vto = kmap_atomic(to, KM_USER1); +- if (cpu_has_dc_aliases && ++ if (cpu_has_dc_aliases && cpu_use_kmap_coherent && + page_mapped(from) && !Page_dcache_dirty(from)) { + vfrom = kmap_coherent(from, vaddr); + copy_page(vto, vfrom); +@@ -230,7 +230,7 @@ void copy_to_user_page(struct vm_area_st + struct page *page, unsigned long vaddr, void *dst, const void *src, + unsigned long len) + { +- if (cpu_has_dc_aliases && ++ if (cpu_has_dc_aliases && cpu_use_kmap_coherent && + page_mapped(page) && !Page_dcache_dirty(page)) { + void *vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); + memcpy(vto, src, len); +@@ -248,7 +248,7 @@ void copy_from_user_page(struct vm_area_ + struct page *page, unsigned long vaddr, void *dst, const void *src, + unsigned long len) + { +- if (cpu_has_dc_aliases && ++ if (cpu_has_dc_aliases && cpu_use_kmap_coherent && + page_mapped(page) && !Page_dcache_dirty(page)) { + void *vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); + memcpy(dst, vfrom, len); diff --git a/target/linux/brcm47xx/patches-3.3/170-fix-74k-cpu.patch b/target/linux/brcm47xx/patches-3.3/170-fix-74k-cpu.patch new file mode 100644 index 000000000..6eee89532 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/170-fix-74k-cpu.patch @@ -0,0 +1,12 @@ +--- a/arch/mips/kernel/cpu-probe.c ++++ b/arch/mips/kernel/cpu-probe.c +@@ -209,9 +209,6 @@ void __init check_wait(void) + break; + + case CPU_74K: +- cpu_wait = r4k_wait; +- if ((c->processor_id & 0xff) >= PRID_REV_ENCODE_332(2, 1, 0)) +- cpu_wait = r4k_wait_irqoff; + break; + + case CPU_TX49XX: diff --git a/target/linux/brcm47xx/patches-3.3/180-USB-OHCI-Add-a-generic-platform-device-driver.patch b/target/linux/brcm47xx/patches-3.3/180-USB-OHCI-Add-a-generic-platform-device-driver.patch new file mode 100644 index 000000000..8ea102f1f --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/180-USB-OHCI-Add-a-generic-platform-device-driver.patch @@ -0,0 +1,271 @@ +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -393,6 +393,16 @@ config USB_CNS3XXX_OHCI + Enable support for the CNS3XXX SOC's on-chip OHCI controller. + It is needed for low-speed USB 1.0 device support. + ++config USB_OHCI_HCD_PLATFORM ++ bool "Generic OHCI driver for a platform device" ++ depends on USB_OHCI_HCD && EXPERIMENTAL ++ default n ++ ---help--- ++ Adds an OHCI host driver for a generic platform device, which ++ provieds a memory space and an irq. ++ ++ If unsure, say N. ++ + config USB_OHCI_BIG_ENDIAN_DESC + bool + depends on USB_OHCI_HCD +--- a/drivers/usb/host/ohci-hcd.c ++++ b/drivers/usb/host/ohci-hcd.c +@@ -1121,6 +1121,11 @@ MODULE_LICENSE ("GPL"); + #define PLATFORM_DRIVER ohci_xls_driver + #endif + ++#ifdef CONFIG_USB_OHCI_HCD_PLATFORM ++#include "ohci-platform.c" ++#define PLATFORM_DRIVER ohci_platform_driver ++#endif ++ + #if !defined(PCI_DRIVER) && \ + !defined(PLATFORM_DRIVER) && \ + !defined(OMAP1_PLATFORM_DRIVER) && \ +--- /dev/null ++++ b/drivers/usb/host/ohci-platform.c +@@ -0,0 +1,194 @@ ++/* ++ * Generic platform ohci driver ++ * ++ * Copyright 2007 Michael Buesch <m@bues.ch> ++ * Copyright 2011-2012 Hauke Mehrtens <hauke@hauke-m.de> ++ * ++ * Derived from the OCHI-SSB driver ++ * Derived from the OHCI-PCI driver ++ * Copyright 1999 Roman Weissgaerber ++ * Copyright 2000-2002 David Brownell ++ * Copyright 1999 Linus Torvalds ++ * Copyright 1999 Gregory P. Smith ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++#include <linux/platform_device.h> ++#include <linux/usb/ohci_pdriver.h> ++ ++static int ohci_platform_reset(struct usb_hcd *hcd) ++{ ++ struct platform_device *pdev = to_platform_device(hcd->self.controller); ++ struct usb_ohci_pdata *pdata = pdev->dev.platform_data; ++ struct ohci_hcd *ohci = hcd_to_ohci(hcd); ++ int err; ++ ++ if (pdata->big_endian_desc) ++ ohci->flags |= OHCI_QUIRK_BE_DESC; ++ if (pdata->big_endian_mmio) ++ ohci->flags |= OHCI_QUIRK_BE_MMIO; ++ if (pdata->no_big_frame_no) ++ ohci->flags |= OHCI_QUIRK_FRAME_NO; ++ ++ ohci_hcd_init(ohci); ++ err = ohci_init(ohci); ++ ++ return err; ++} ++ ++static int ohci_platform_start(struct usb_hcd *hcd) ++{ ++ struct ohci_hcd *ohci = hcd_to_ohci(hcd); ++ int err; ++ ++ err = ohci_run(ohci); ++ if (err < 0) { ++ ohci_err(ohci, "can't start\n"); ++ ohci_stop(hcd); ++ } ++ ++ return err; ++} ++ ++static const struct hc_driver ohci_platform_hc_driver = { ++ .description = hcd_name, ++ .product_desc = "Generic Platform OHCI Controller", ++ .hcd_priv_size = sizeof(struct ohci_hcd), ++ ++ .irq = ohci_irq, ++ .flags = HCD_MEMORY | HCD_USB11, ++ ++ .reset = ohci_platform_reset, ++ .start = ohci_platform_start, ++ .stop = ohci_stop, ++ .shutdown = ohci_shutdown, ++ ++ .urb_enqueue = ohci_urb_enqueue, ++ .urb_dequeue = ohci_urb_dequeue, ++ .endpoint_disable = ohci_endpoint_disable, ++ ++ .get_frame_number = ohci_get_frame, ++ ++ .hub_status_data = ohci_hub_status_data, ++ .hub_control = ohci_hub_control, ++#ifdef CONFIG_PM ++ .bus_suspend = ohci_bus_suspend, ++ .bus_resume = ohci_bus_resume, ++#endif ++ ++ .start_port_reset = ohci_start_port_reset, ++}; ++ ++static int __devinit ohci_platform_probe(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd; ++ struct resource *res_mem; ++ int irq; ++ int err = -ENOMEM; ++ ++ BUG_ON(!dev->dev.platform_data); ++ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ irq = platform_get_irq(dev, 0); ++ if (irq < 0) { ++ pr_err("no irq provieded"); ++ return irq; ++ } ++ ++ res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); ++ if (!res_mem) { ++ pr_err("no memory recourse provieded"); ++ return -ENXIO; ++ } ++ ++ hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, ++ dev_name(&dev->dev)); ++ if (!hcd) ++ return -ENOMEM; ++ ++ hcd->rsrc_start = res_mem->start; ++ hcd->rsrc_len = resource_size(res_mem); ++ ++ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { ++ pr_err("controller already in use"); ++ err = -EBUSY; ++ goto err_put_hcd; ++ } ++ ++ hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); ++ if (!hcd->regs) ++ goto err_release_region; ++ err = usb_add_hcd(hcd, irq, IRQF_SHARED); ++ if (err) ++ goto err_iounmap; ++ ++ platform_set_drvdata(dev, hcd); ++ ++ return err; ++ ++err_iounmap: ++ iounmap(hcd->regs); ++err_release_region: ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++err_put_hcd: ++ usb_put_hcd(hcd); ++ return err; ++} ++ ++static int __devexit ohci_platform_remove(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(dev); ++ ++ usb_remove_hcd(hcd); ++ iounmap(hcd->regs); ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++ usb_put_hcd(hcd); ++ platform_set_drvdata(dev, NULL); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++ ++static int ohci_platform_suspend(struct device *dev) ++{ ++ return 0; ++} ++ ++static int ohci_platform_resume(struct device *dev) ++{ ++ struct usb_hcd *hcd = dev_get_drvdata(dev); ++ ++ ohci_finish_controller_resume(hcd); ++ return 0; ++} ++ ++#else /* !CONFIG_PM */ ++#define ohci_platform_suspend NULL ++#define ohci_platform_resume NULL ++#endif /* CONFIG_PM */ ++ ++static const struct platform_device_id ohci_platform_table[] = { ++ { "ohci-platform", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(platform, ohci_platform_table); ++ ++static const struct dev_pm_ops ohci_platform_pm_ops = { ++ .suspend = ohci_platform_suspend, ++ .resume = ohci_platform_resume, ++}; ++ ++static struct platform_driver ohci_platform_driver = { ++ .id_table = ohci_platform_table, ++ .probe = ohci_platform_probe, ++ .remove = __devexit_p(ohci_platform_remove), ++ .shutdown = usb_hcd_platform_shutdown, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ohci-platform", ++ .pm = &ohci_platform_pm_ops, ++ } ++}; +--- /dev/null ++++ b/include/linux/usb/ohci_pdriver.h +@@ -0,0 +1,38 @@ ++/* ++ * Copyright (C) 2012 Hauke Mehrtens <hauke@hauke-m.de> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software Foundation, ++ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __USB_CORE_OHCI_PDRIVER_H ++#define __USB_CORE_OHCI_PDRIVER_H ++ ++/** ++ * struct usb_ohci_pdata - platform_data for generic ohci driver ++ * ++ * @big_endian_desc: BE descriptors ++ * @big_endian_mmio: BE registers ++ * @no_big_frame_no: no big endian frame_no shift ++ * ++ * These are general configuration options for the OHCI controller. All of ++ * these options are activating more or less workarounds for some hardware. ++ */ ++struct usb_ohci_pdata { ++ unsigned big_endian_desc:1; ++ unsigned big_endian_mmio:1; ++ unsigned no_big_frame_no:1; ++}; ++ ++#endif /* __USB_CORE_OHCI_PDRIVER_H */ diff --git a/target/linux/brcm47xx/patches-3.3/181-USB-EHCI-Add-a-generic-platform-device-driver.patch b/target/linux/brcm47xx/patches-3.3/181-USB-EHCI-Add-a-generic-platform-device-driver.patch new file mode 100644 index 000000000..62eb87a8a --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/181-USB-EHCI-Add-a-generic-platform-device-driver.patch @@ -0,0 +1,283 @@ +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -403,6 +403,16 @@ config USB_OHCI_HCD_PLATFORM + + If unsure, say N. + ++config USB_EHCI_HCD_PLATFORM ++ bool "Generic EHCI driver for a platform device" ++ depends on USB_EHCI_HCD && EXPERIMENTAL ++ default n ++ ---help--- ++ Adds an EHCI host driver for a generic platform device, which ++ provieds a memory space and an irq. ++ ++ If unsure, say N. ++ + config USB_OHCI_BIG_ENDIAN_DESC + bool + depends on USB_OHCI_HCD +--- a/drivers/usb/host/ehci-hcd.c ++++ b/drivers/usb/host/ehci-hcd.c +@@ -1381,6 +1381,11 @@ MODULE_LICENSE ("GPL"); + #define PLATFORM_DRIVER ehci_mv_driver + #endif + ++#ifdef CONFIG_USB_EHCI_HCD_PLATFORM ++#include "ehci-platform.c" ++#define PLATFORM_DRIVER ehci_platform_driver ++#endif ++ + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ + !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ + !defined(XILINX_OF_PLATFORM_DRIVER) +--- /dev/null ++++ b/drivers/usb/host/ehci-platform.c +@@ -0,0 +1,198 @@ ++/* ++ * Generic platform ehci driver ++ * ++ * Copyright 2007 Steven Brown <sbrown@cortland.com> ++ * Copyright 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> ++ * ++ * Derived from the ohci-ssb driver ++ * Copyright 2007 Michael Buesch <m@bues.ch> ++ * ++ * Derived from the EHCI-PCI driver ++ * Copyright (c) 2000-2004 by David Brownell ++ * ++ * Derived from the ohci-pci driver ++ * Copyright 1999 Roman Weissgaerber ++ * Copyright 2000-2002 David Brownell ++ * Copyright 1999 Linus Torvalds ++ * Copyright 1999 Gregory P. Smith ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++#include <linux/platform_device.h> ++#include <linux/usb/ehci_pdriver.h> ++ ++static int ehci_platform_reset(struct usb_hcd *hcd) ++{ ++ struct platform_device *pdev = to_platform_device(hcd->self.controller); ++ struct usb_ehci_pdata *pdata = pdev->dev.platform_data; ++ struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++ int retval; ++ ++ hcd->has_tt = pdata->has_tt; ++ ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug; ++ ehci->big_endian_desc = pdata->big_endian_desc; ++ ehci->big_endian_mmio = pdata->big_endian_mmio; ++ ++ ehci->caps = hcd->regs + pdata->caps_offset; ++ retval = ehci_setup(hcd); ++ if (retval) ++ return retval; ++ ++ if (pdata->port_power_on) ++ ehci_port_power(ehci, 1); ++ if (pdata->port_power_off) ++ ehci_port_power(ehci, 0); ++ ++ return 0; ++} ++ ++static const struct hc_driver ehci_platform_hc_driver = { ++ .description = hcd_name, ++ .product_desc = "Generic Platform EHCI Controller", ++ .hcd_priv_size = sizeof(struct ehci_hcd), ++ ++ .irq = ehci_irq, ++ .flags = HCD_MEMORY | HCD_USB2, ++ ++ .reset = ehci_platform_reset, ++ .start = ehci_run, ++ .stop = ehci_stop, ++ .shutdown = ehci_shutdown, ++ ++ .urb_enqueue = ehci_urb_enqueue, ++ .urb_dequeue = ehci_urb_dequeue, ++ .endpoint_disable = ehci_endpoint_disable, ++ .endpoint_reset = ehci_endpoint_reset, ++ ++ .get_frame_number = ehci_get_frame, ++ ++ .hub_status_data = ehci_hub_status_data, ++ .hub_control = ehci_hub_control, ++#if defined(CONFIG_PM) ++ .bus_suspend = ehci_bus_suspend, ++ .bus_resume = ehci_bus_resume, ++#endif ++ .relinquish_port = ehci_relinquish_port, ++ .port_handed_over = ehci_port_handed_over, ++ ++ .update_device = ehci_update_device, ++ ++ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, ++}; ++ ++static int __devinit ehci_platform_probe(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd; ++ struct resource *res_mem; ++ int irq; ++ int err = -ENOMEM; ++ ++ BUG_ON(!dev->dev.platform_data); ++ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ irq = platform_get_irq(dev, 0); ++ if (irq < 0) { ++ pr_err("no irq provieded"); ++ return irq; ++ } ++ res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); ++ if (!res_mem) { ++ pr_err("no memory recourse provieded"); ++ return -ENXIO; ++ } ++ ++ hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, ++ dev_name(&dev->dev)); ++ if (!hcd) ++ return -ENOMEM; ++ ++ hcd->rsrc_start = res_mem->start; ++ hcd->rsrc_len = resource_size(res_mem); ++ ++ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { ++ pr_err("controller already in use"); ++ err = -EBUSY; ++ goto err_put_hcd; ++ } ++ ++ hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); ++ if (!hcd->regs) ++ goto err_release_region; ++ err = usb_add_hcd(hcd, irq, IRQF_SHARED); ++ if (err) ++ goto err_iounmap; ++ ++ platform_set_drvdata(dev, hcd); ++ ++ return err; ++ ++err_iounmap: ++ iounmap(hcd->regs); ++err_release_region: ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++err_put_hcd: ++ usb_put_hcd(hcd); ++ return err; ++} ++ ++static int __devexit ehci_platform_remove(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(dev); ++ ++ usb_remove_hcd(hcd); ++ iounmap(hcd->regs); ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++ usb_put_hcd(hcd); ++ platform_set_drvdata(dev, NULL); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++ ++static int ehci_platform_suspend(struct device *dev) ++{ ++ struct usb_hcd *hcd = dev_get_drvdata(dev); ++ bool wakeup = device_may_wakeup(dev); ++ ++ ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), wakeup); ++ return 0; ++} ++ ++static int ehci_platform_resume(struct device *dev) ++{ ++ struct usb_hcd *hcd = dev_get_drvdata(dev); ++ ++ ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd)); ++ return 0; ++} ++ ++#else /* !CONFIG_PM */ ++#define ehci_platform_suspend NULL ++#define ehci_platform_resume NULL ++#endif /* CONFIG_PM */ ++ ++static const struct platform_device_id ehci_platform_table[] = { ++ { "ehci-platform", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(platform, ehci_platform_table); ++ ++static const struct dev_pm_ops ehci_platform_pm_ops = { ++ .suspend = ehci_platform_suspend, ++ .resume = ehci_platform_resume, ++}; ++ ++static struct platform_driver ehci_platform_driver = { ++ .id_table = ehci_platform_table, ++ .probe = ehci_platform_probe, ++ .remove = __devexit_p(ehci_platform_remove), ++ .shutdown = usb_hcd_platform_shutdown, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ehci-platform", ++ .pm = &ehci_platform_pm_ops, ++ } ++}; +--- /dev/null ++++ b/include/linux/usb/ehci_pdriver.h +@@ -0,0 +1,46 @@ ++/* ++ * Copyright (C) 2012 Hauke Mehrtens <hauke@hauke-m.de> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software Foundation, ++ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __USB_CORE_EHCI_PDRIVER_H ++#define __USB_CORE_EHCI_PDRIVER_H ++ ++/** ++ * struct usb_ehci_pdata - platform_data for generic ehci driver ++ * ++ * @caps_offset: offset of the EHCI Capability Registers to the start of ++ * the io memory region provided to the driver. ++ * @has_tt: set to 1 if TT is integrated in root hub. ++ * @port_power_on: set to 1 if the controller needs a power up after ++ * initialization. ++ * @port_power_off: set to 1 if the controller needs to be powered down ++ * after initialization. ++ * ++ * These are general configuration options for the EHCI controller. All of ++ * these options are activating more or less workarounds for some hardware. ++ */ ++struct usb_ehci_pdata { ++ int caps_offset; ++ unsigned has_tt:1; ++ unsigned has_synopsys_hc_bug:1; ++ unsigned big_endian_desc:1; ++ unsigned big_endian_mmio:1; ++ unsigned port_power_on:1; ++ unsigned port_power_off:1; ++}; ++ ++#endif /* __USB_CORE_EHCI_PDRIVER_H */ diff --git a/target/linux/brcm47xx/patches-3.3/183-USB-Add-driver-for-the-bcma-bus.patch b/target/linux/brcm47xx/patches-3.3/183-USB-Add-driver-for-the-bcma-bus.patch new file mode 100644 index 000000000..c021d503a --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/183-USB-Add-driver-for-the-bcma-bus.patch @@ -0,0 +1,362 @@ +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -633,3 +633,15 @@ config USB_PXA168_EHCI + help + Enable support for Marvell PXA168 SoC's on-chip EHCI + host controller ++ ++config USB_HCD_BCMA ++ tristate "BCMA usb host driver" ++ depends on BCMA && EXPERIMENTAL ++ select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD ++ select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD ++ help ++ Enbale support for the EHCI and OCHI host controller on an bcma bus. ++ It converts the bcma driver into two platform device drivers ++ for ehci and ohci. ++ ++ If unsure, say N. +--- a/drivers/usb/host/Makefile ++++ b/drivers/usb/host/Makefile +@@ -37,3 +37,4 @@ obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd + obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o + obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o + obj-$(CONFIG_MIPS_ALCHEMY) += alchemy-common.o ++obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o +--- /dev/null ++++ b/drivers/usb/host/bcma-hcd.c +@@ -0,0 +1,334 @@ ++/* ++ * Broadcom specific Advanced Microcontroller Bus ++ * Broadcom USB-core driver (BCMA bus glue) ++ * ++ * Copyright 2011-2012 Hauke Mehrtens <hauke@hauke-m.de> ++ * ++ * Based on ssb-ohci driver ++ * Copyright 2007 Michael Buesch <m@bues.ch> ++ * ++ * Derived from the OHCI-PCI driver ++ * Copyright 1999 Roman Weissgaerber ++ * Copyright 2000-2002 David Brownell ++ * Copyright 1999 Linus Torvalds ++ * Copyright 1999 Gregory P. Smith ++ * ++ * Derived from the USBcore related parts of Broadcom-SB ++ * Copyright 2005-2011 Broadcom Corporation ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++#include <linux/bcma/bcma.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <linux/module.h> ++#include <linux/usb/ehci_pdriver.h> ++#include <linux/usb/ohci_pdriver.h> ++ ++MODULE_AUTHOR("Hauke Mehrtens"); ++MODULE_DESCRIPTION("Common USB driver for BCMA Bus"); ++MODULE_LICENSE("GPL"); ++ ++struct bcma_hcd_device { ++ struct platform_device *ehci_dev; ++ struct platform_device *ohci_dev; ++}; ++ ++/* Wait for bitmask in a register to get set or cleared. ++ * timeout is in units of ten-microseconds. ++ */ ++static int bcma_wait_bits(struct bcma_device *dev, u16 reg, u32 bitmask, ++ int timeout) ++{ ++ int i; ++ u32 val; ++ ++ for (i = 0; i < timeout; i++) { ++ val = bcma_read32(dev, reg); ++ if ((val & bitmask) == bitmask) ++ return 0; ++ udelay(10); ++ } ++ ++ return -ETIMEDOUT; ++} ++ ++static void __devinit bcma_hcd_4716wa(struct bcma_device *dev) ++{ ++#ifdef CONFIG_BCMA_DRIVER_MIPS ++ /* Work around for 4716 failures. */ ++ if (dev->bus->chipinfo.id == 0x4716) { ++ u32 tmp; ++ ++ tmp = bcma_cpu_clock(&dev->bus->drv_mips); ++ if (tmp >= 480000000) ++ tmp = 0x1846b; /* set CDR to 0x11(fast) */ ++ else if (tmp == 453000000) ++ tmp = 0x1046b; /* set CDR to 0x10(slow) */ ++ else ++ tmp = 0; ++ ++ /* Change Shim mdio control reg to fix host not acking at ++ * high frequencies ++ */ ++ if (tmp) { ++ bcma_write32(dev, 0x524, 0x1); /* write sel to enable */ ++ udelay(500); ++ ++ bcma_write32(dev, 0x524, tmp); ++ udelay(500); ++ bcma_write32(dev, 0x524, 0x4ab); ++ udelay(500); ++ bcma_read32(dev, 0x528); ++ bcma_write32(dev, 0x528, 0x80000000); ++ } ++ } ++#endif /* CONFIG_BCMA_DRIVER_MIPS */ ++} ++ ++/* based on arch/mips/brcm-boards/bcm947xx/pcibios.c */ ++static void __devinit bcma_hcd_init_chip(struct bcma_device *dev) ++{ ++ u32 tmp; ++ ++ /* ++ * USB 2.0 special considerations: ++ * ++ * 1. Since the core supports both OHCI and EHCI functions, it must ++ * only be reset once. ++ * ++ * 2. In addition to the standard SI reset sequence, the Host Control ++ * Register must be programmed to bring the USB core and various ++ * phy components out of reset. ++ */ ++ if (!bcma_core_is_enabled(dev)) { ++ bcma_core_enable(dev, 0); ++ mdelay(10); ++ if (dev->id.rev >= 5) { ++ /* Enable Misc PLL */ ++ tmp = bcma_read32(dev, 0x1e0); ++ tmp |= 0x100; ++ bcma_write32(dev, 0x1e0, tmp); ++ if (bcma_wait_bits(dev, 0x1e0, 1 << 24, 100)) ++ printk(KERN_EMERG "Failed to enable misc PPL!\n"); ++ ++ /* Take out of resets */ ++ bcma_write32(dev, 0x200, 0x4ff); ++ udelay(25); ++ bcma_write32(dev, 0x200, 0x6ff); ++ udelay(25); ++ ++ /* Make sure digital and AFE are locked in USB PHY */ ++ bcma_write32(dev, 0x524, 0x6b); ++ udelay(50); ++ tmp = bcma_read32(dev, 0x524); ++ udelay(50); ++ bcma_write32(dev, 0x524, 0xab); ++ udelay(50); ++ tmp = bcma_read32(dev, 0x524); ++ udelay(50); ++ bcma_write32(dev, 0x524, 0x2b); ++ udelay(50); ++ tmp = bcma_read32(dev, 0x524); ++ udelay(50); ++ bcma_write32(dev, 0x524, 0x10ab); ++ udelay(50); ++ tmp = bcma_read32(dev, 0x524); ++ ++ if (bcma_wait_bits(dev, 0x528, 0xc000, 10000)) { ++ tmp = bcma_read32(dev, 0x528); ++ printk(KERN_EMERG ++ "USB20H mdio_rddata 0x%08x\n", tmp); ++ } ++ bcma_write32(dev, 0x528, 0x80000000); ++ tmp = bcma_read32(dev, 0x314); ++ udelay(265); ++ bcma_write32(dev, 0x200, 0x7ff); ++ udelay(10); ++ ++ /* Take USB and HSIC out of non-driving modes */ ++ bcma_write32(dev, 0x510, 0); ++ } else { ++ bcma_write32(dev, 0x200, 0x7ff); ++ ++ udelay(1); ++ } ++ ++ bcma_hcd_4716wa(dev); ++ } ++} ++ ++static const struct usb_ehci_pdata ehci_pdata = { ++}; ++ ++static const struct usb_ohci_pdata ohci_pdata = { ++}; ++ ++static struct platform_device * __devinit ++bcma_hcd_create_pdev(struct bcma_device *dev, bool ohci, u32 addr) ++{ ++ struct platform_device *hci_dev; ++ struct resource hci_res[2]; ++ int ret = -ENOMEM; ++ ++ memset(hci_res, 0, sizeof(hci_res)); ++ ++ hci_res[0].start = addr; ++ hci_res[0].end = hci_res[0].start + 0x1000 - 1; ++ hci_res[0].flags = IORESOURCE_MEM; ++ ++ hci_res[1].start = dev->irq; ++ hci_res[1].flags = IORESOURCE_IRQ; ++ ++ hci_dev = platform_device_alloc(ohci ? "ohci-platform" : ++ "ehci-platform" , 0); ++ if (!hci_dev) ++ return NULL; ++ ++ hci_dev->dev.parent = &dev->dev; ++ hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask; ++ ++ ret = platform_device_add_resources(hci_dev, hci_res, ++ ARRAY_SIZE(hci_res)); ++ if (ret) ++ goto err_alloc; ++ if (ohci) ++ ret = platform_device_add_data(hci_dev, &ohci_pdata, ++ sizeof(ohci_pdata)); ++ else ++ ret = platform_device_add_data(hci_dev, &ehci_pdata, ++ sizeof(ehci_pdata)); ++ if (ret) ++ goto err_alloc; ++ ret = platform_device_add(hci_dev); ++ if (ret) ++ goto err_alloc; ++ ++ return hci_dev; ++ ++err_alloc: ++ platform_device_put(hci_dev); ++ return ERR_PTR(ret); ++} ++ ++static int __devinit bcma_hcd_probe(struct bcma_device *dev) ++{ ++ int err; ++ u16 chipid_top; ++ u32 ohci_addr; ++ struct bcma_hcd_device *usb_dev; ++ struct bcma_chipinfo *chipinfo; ++ ++ chipinfo = &dev->bus->chipinfo; ++ /* USBcores are only connected on embedded devices. */ ++ chipid_top = (chipinfo->id & 0xFF00); ++ if (chipid_top != 0x4700 && chipid_top != 0x5300) ++ return -ENODEV; ++ ++ /* TODO: Probably need checks here; is the core connected? */ ++ ++ if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) || ++ dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32))) ++ return -EOPNOTSUPP; ++ ++ usb_dev = kzalloc(sizeof(struct bcma_hcd_device), GFP_KERNEL); ++ if (!usb_dev) ++ return -ENOMEM; ++ ++ bcma_hcd_init_chip(dev); ++ ++ /* In AI chips EHCI is addrspace 0, OHCI is 1 */ ++ ohci_addr = dev->addr1; ++ if ((chipinfo->id == 0x5357 || chipinfo->id == 0x4749) ++ && chipinfo->rev == 0) ++ ohci_addr = 0x18009000; ++ ++ usb_dev->ohci_dev = bcma_hcd_create_pdev(dev, true, ohci_addr); ++ if (IS_ERR(usb_dev->ohci_dev)) { ++ err = PTR_ERR(usb_dev->ohci_dev); ++ goto err_free_usb_dev; ++ } ++ ++ usb_dev->ehci_dev = bcma_hcd_create_pdev(dev, false, dev->addr); ++ if (IS_ERR(usb_dev->ehci_dev)) { ++ err = PTR_ERR(usb_dev->ehci_dev); ++ goto err_unregister_ohci_dev; ++ } ++ ++ bcma_set_drvdata(dev, usb_dev); ++ return 0; ++ ++err_unregister_ohci_dev: ++ platform_device_unregister(usb_dev->ohci_dev); ++err_free_usb_dev: ++ kfree(usb_dev); ++ return err; ++} ++ ++static void __devexit bcma_hcd_remove(struct bcma_device *dev) ++{ ++ struct bcma_hcd_device *usb_dev = bcma_get_drvdata(dev); ++ struct platform_device *ohci_dev = usb_dev->ohci_dev; ++ struct platform_device *ehci_dev = usb_dev->ehci_dev; ++ ++ if (ohci_dev) ++ platform_device_unregister(ohci_dev); ++ if (ehci_dev) ++ platform_device_unregister(ehci_dev); ++ ++ bcma_core_disable(dev, 0); ++} ++ ++static void bcma_hcd_shutdown(struct bcma_device *dev) ++{ ++ bcma_core_disable(dev, 0); ++} ++ ++#ifdef CONFIG_PM ++ ++static int bcma_hcd_suspend(struct bcma_device *dev, pm_message_t state) ++{ ++ bcma_core_disable(dev, 0); ++ ++ return 0; ++} ++ ++static int bcma_hcd_resume(struct bcma_device *dev) ++{ ++ bcma_core_enable(dev, 0); ++ ++ return 0; ++} ++ ++#else /* !CONFIG_PM */ ++#define bcma_hcd_suspend NULL ++#define bcma_hcd_resume NULL ++#endif /* CONFIG_PM */ ++ ++static const struct bcma_device_id bcma_hcd_table[] __devinitconst = { ++ BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_USB20_HOST, BCMA_ANY_REV, BCMA_ANY_CLASS), ++ BCMA_CORETABLE_END ++}; ++MODULE_DEVICE_TABLE(bcma, bcma_hcd_table); ++ ++static struct bcma_driver bcma_hcd_driver = { ++ .name = KBUILD_MODNAME, ++ .id_table = bcma_hcd_table, ++ .probe = bcma_hcd_probe, ++ .remove = __devexit_p(bcma_hcd_remove), ++ .shutdown = bcma_hcd_shutdown, ++ .suspend = bcma_hcd_suspend, ++ .resume = bcma_hcd_resume, ++}; ++ ++static int __init bcma_hcd_init(void) ++{ ++ return bcma_driver_register(&bcma_hcd_driver); ++} ++module_init(bcma_hcd_init); ++ ++static void __exit bcma_hcd_exit(void) ++{ ++ bcma_driver_unregister(&bcma_hcd_driver); ++} ++module_exit(bcma_hcd_exit); diff --git a/target/linux/brcm47xx/patches-3.3/184-USB-Add-driver-for-the-ssb-bus.patch b/target/linux/brcm47xx/patches-3.3/184-USB-Add-driver-for-the-ssb-bus.patch new file mode 100644 index 000000000..ecbed777b --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/184-USB-Add-driver-for-the-ssb-bus.patch @@ -0,0 +1,308 @@ + +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -645,3 +645,15 @@ config USB_HCD_BCMA + for ehci and ohci. + + If unsure, say N. ++ ++config USB_HCD_SSB ++ tristate "SSB usb host driver" ++ depends on SSB && EXPERIMENTAL ++ select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD ++ select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD ++ help ++ Enbale support for the EHCI and OCHI host controller on an bcma bus. ++ It converts the bcma driver into two platform device drivers ++ for ehci and ohci. ++ ++ If unsure, say N. +--- a/drivers/usb/host/Makefile ++++ b/drivers/usb/host/Makefile +@@ -38,3 +38,4 @@ obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-m + obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o + obj-$(CONFIG_MIPS_ALCHEMY) += alchemy-common.o + obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o ++obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o +--- /dev/null ++++ b/drivers/usb/host/ssb-hcd.c +@@ -0,0 +1,279 @@ ++/* ++ * Sonics Silicon Backplane ++ * Broadcom USB-core driver (SSB bus glue) ++ * ++ * Copyright 2011-2012 Hauke Mehrtens <hauke@hauke-m.de> ++ * ++ * Based on ssb-ohci driver ++ * Copyright 2007 Michael Buesch <m@bues.ch> ++ * ++ * Derived from the OHCI-PCI driver ++ * Copyright 1999 Roman Weissgaerber ++ * Copyright 2000-2002 David Brownell ++ * Copyright 1999 Linus Torvalds ++ * Copyright 1999 Gregory P. Smith ++ * ++ * Derived from the USBcore related parts of Broadcom-SB ++ * Copyright 2005-2011 Broadcom Corporation ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++#include <linux/ssb/ssb.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <linux/module.h> ++#include <linux/usb/ehci_pdriver.h> ++#include <linux/usb/ohci_pdriver.h> ++ ++MODULE_AUTHOR("Hauke Mehrtens"); ++MODULE_DESCRIPTION("Common USB driver for SSB Bus"); ++MODULE_LICENSE("GPL"); ++ ++#define SSB_HCD_TMSLOW_HOSTMODE (1 << 29) ++ ++struct ssb_hcd_device { ++ struct platform_device *ehci_dev; ++ struct platform_device *ohci_dev; ++ ++ u32 enable_flags; ++}; ++ ++static void __devinit ssb_hcd_5354wa(struct ssb_device *dev) ++{ ++#ifdef CONFIG_SSB_DRIVER_MIPS ++ /* Work around for 5354 failures */ ++ if (dev->id.revision == 2 && dev->bus->chip_id == 0x5354) { ++ /* Change syn01 reg */ ++ ssb_write32(dev, 0x894, 0x00fe00fe); ++ ++ /* Change syn03 reg */ ++ ssb_write32(dev, 0x89c, ssb_read32(dev, 0x89c) | 0x1); ++ } ++#endif ++} ++ ++static void __devinit ssb_hcd_usb20wa(struct ssb_device *dev) ++{ ++ if (dev->id.coreid == SSB_DEV_USB20_HOST) { ++ /* ++ * USB 2.0 special considerations: ++ * ++ * In addition to the standard SSB reset sequence, the Host ++ * Control Register must be programmed to bring the USB core ++ * and various phy components out of reset. ++ */ ++ ssb_write32(dev, 0x200, 0x7ff); ++ ++ /* Change Flush control reg */ ++ ssb_write32(dev, 0x400, ssb_read32(dev, 0x400) & ~8); ++ ssb_read32(dev, 0x400); ++ ++ /* Change Shim control reg */ ++ ssb_write32(dev, 0x304, ssb_read32(dev, 0x304) & ~0x100); ++ ssb_read32(dev, 0x304); ++ ++ udelay(1); ++ ++ ssb_hcd_5354wa(dev); ++ } ++} ++ ++/* based on arch/mips/brcm-boards/bcm947xx/pcibios.c */ ++static u32 __devinit ssb_hcd_init_chip(struct ssb_device *dev) ++{ ++ u32 flags = 0; ++ ++ if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) ++ /* Put the device into host-mode. */ ++ flags |= SSB_HCD_TMSLOW_HOSTMODE; ++ ++ ssb_device_enable(dev, flags); ++ ++ ssb_hcd_usb20wa(dev); ++ ++ return flags; ++} ++ ++static const struct usb_ehci_pdata ehci_pdata = { ++}; ++ ++static const struct usb_ohci_pdata ohci_pdata = { ++}; ++ ++static struct platform_device * __devinit ++ssb_hcd_create_pdev(struct ssb_device *dev, bool ohci, u32 addr, u32 len) ++{ ++ struct platform_device *hci_dev; ++ struct resource hci_res[2]; ++ int ret = -ENOMEM; ++ ++ memset(hci_res, 0, sizeof(hci_res)); ++ ++ hci_res[0].start = addr; ++ hci_res[0].end = hci_res[0].start + len - 1; ++ hci_res[0].flags = IORESOURCE_MEM; ++ ++ hci_res[1].start = dev->irq; ++ hci_res[1].flags = IORESOURCE_IRQ; ++ ++ hci_dev = platform_device_alloc(ohci ? "ohci-platform" : ++ "ehci-platform" , 0); ++ if (!hci_dev) ++ return NULL; ++ ++ hci_dev->dev.parent = dev->dev; ++ hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask; ++ ++ ret = platform_device_add_resources(hci_dev, hci_res, ++ ARRAY_SIZE(hci_res)); ++ if (ret) ++ goto err_alloc; ++ if (ohci) ++ ret = platform_device_add_data(hci_dev, &ohci_pdata, ++ sizeof(ohci_pdata)); ++ else ++ ret = platform_device_add_data(hci_dev, &ehci_pdata, ++ sizeof(ehci_pdata)); ++ if (ret) ++ goto err_alloc; ++ ret = platform_device_add(hci_dev); ++ if (ret) ++ goto err_alloc; ++ ++ return hci_dev; ++ ++err_alloc: ++ platform_device_put(hci_dev); ++ return ERR_PTR(ret); ++} ++ ++static int __devinit ssb_hcd_probe(struct ssb_device *dev, ++ const struct ssb_device_id *id) ++{ ++ int err, tmp; ++ int start, len; ++ u16 chipid_top; ++ u16 coreid = dev->id.coreid; ++ struct ssb_hcd_device *usb_dev; ++ ++ /* USBcores are only connected on embedded devices. */ ++ chipid_top = (dev->bus->chip_id & 0xFF00); ++ if (chipid_top != 0x4700 && chipid_top != 0x5300) ++ return -ENODEV; ++ ++ /* TODO: Probably need checks here; is the core connected? */ ++ ++ if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) || ++ dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32))) ++ return -EOPNOTSUPP; ++ ++ usb_dev = kzalloc(sizeof(struct ssb_hcd_device), GFP_KERNEL); ++ if (!usb_dev) ++ return -ENOMEM; ++ ++ /* We currently always attach SSB_DEV_USB11_HOSTDEV ++ * as HOST OHCI. If we want to attach it as Client device, ++ * we must branch here and call into the (yet to ++ * be written) Client mode driver. Same for remove(). */ ++ usb_dev->enable_flags = ssb_hcd_init_chip(dev); ++ ++ tmp = ssb_read32(dev, SSB_ADMATCH0); ++ ++ start = ssb_admatch_base(tmp); ++ len = (coreid == SSB_DEV_USB20_HOST) ? 0x800 : ssb_admatch_size(tmp); ++ usb_dev->ohci_dev = ssb_hcd_create_pdev(dev, true, start, len); ++ if (IS_ERR(usb_dev->ohci_dev)) { ++ err = PTR_ERR(usb_dev->ohci_dev); ++ goto err_free_usb_dev; ++ } ++ ++ if (coreid == SSB_DEV_USB20_HOST) { ++ start = ssb_admatch_base(tmp) + 0x800; /* ehci core offset */ ++ usb_dev->ehci_dev = ssb_hcd_create_pdev(dev, false, start, len); ++ if (IS_ERR(usb_dev->ehci_dev)) { ++ err = PTR_ERR(usb_dev->ehci_dev); ++ goto err_unregister_ohci_dev; ++ } ++ } ++ ++ ssb_set_drvdata(dev, usb_dev); ++ return 0; ++ ++err_unregister_ohci_dev: ++ platform_device_unregister(usb_dev->ohci_dev); ++err_free_usb_dev: ++ kfree(usb_dev); ++ return err; ++} ++ ++static void __devexit ssb_hcd_remove(struct ssb_device *dev) ++{ ++ struct ssb_hcd_device *usb_dev = ssb_get_drvdata(dev); ++ struct platform_device *ohci_dev = usb_dev->ohci_dev; ++ struct platform_device *ehci_dev = usb_dev->ehci_dev; ++ ++ if (ohci_dev) ++ platform_device_unregister(ohci_dev); ++ if (ehci_dev) ++ platform_device_unregister(ehci_dev); ++ ++ ssb_device_disable(dev, 0); ++} ++ ++static void __devexit ssb_hcd_shutdown(struct ssb_device *dev) ++{ ++ ssb_device_disable(dev, 0); ++} ++ ++#ifdef CONFIG_PM ++ ++static int ssb_hcd_suspend(struct ssb_device *dev, pm_message_t state) ++{ ++ ssb_device_disable(dev, 0); ++ ++ return 0; ++} ++ ++static int ssb_hcd_resume(struct ssb_device *dev) ++{ ++ struct ssb_hcd_device *usb_dev = ssb_get_drvdata(dev); ++ ++ ssb_device_enable(dev, usb_dev->enable_flags); ++ ++ return 0; ++} ++ ++#else /* !CONFIG_PM */ ++#define ssb_hcd_suspend NULL ++#define ssb_hcd_resume NULL ++#endif /* CONFIG_PM */ ++ ++static const struct ssb_device_id ssb_hcd_table[] __devinitconst = { ++ SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV), ++ SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV), ++ SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV), ++ SSB_DEVTABLE_END ++}; ++MODULE_DEVICE_TABLE(ssb, ssb_hcd_table); ++ ++static struct ssb_driver ssb_hcd_driver = { ++ .name = KBUILD_MODNAME, ++ .id_table = ssb_hcd_table, ++ .probe = ssb_hcd_probe, ++ .remove = __devexit_p(ssb_hcd_remove), ++ .shutdown = ssb_hcd_shutdown, ++ .suspend = ssb_hcd_suspend, ++ .resume = ssb_hcd_resume, ++}; ++ ++static int __init ssb_hcd_init(void) ++{ ++ return ssb_driver_register(&ssb_hcd_driver); ++} ++module_init(ssb_hcd_init); ++ ++static void __exit ssb_hcd_exit(void) ++{ ++ ssb_driver_unregister(&ssb_hcd_driver); ++} ++module_exit(ssb_hcd_exit); diff --git a/target/linux/brcm47xx/patches-3.3/185-USB-OHCI-remove-old-SSB-OHCI-driver.patch b/target/linux/brcm47xx/patches-3.3/185-USB-OHCI-remove-old-SSB-OHCI-driver.patch new file mode 100644 index 000000000..645d40941 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/185-USB-OHCI-remove-old-SSB-OHCI-driver.patch @@ -0,0 +1,340 @@ +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -361,10 +361,15 @@ config USB_OHCI_HCD_PCI + If unsure, say Y. + + config USB_OHCI_HCD_SSB +- bool "OHCI support for Broadcom SSB OHCI core" ++ bool "OHCI support for Broadcom SSB OHCI core (DEPRECATED)" + depends on USB_OHCI_HCD && (SSB = y || SSB = USB_OHCI_HCD) && EXPERIMENTAL ++ select USB_HCD_SSB ++ select USB_OHCI_HCD_PLATFORM + default n + ---help--- ++ This option is deprecated now and the driver was removed, use ++ USB_HCD_SSB and USB_OHCI_HCD_PLATFORM instead. ++ + Support for the Sonics Silicon Backplane (SSB) attached + Broadcom USB OHCI core. + +--- a/drivers/usb/host/ohci-hcd.c ++++ b/drivers/usb/host/ohci-hcd.c +@@ -1081,11 +1081,6 @@ MODULE_LICENSE ("GPL"); + #define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver + #endif + +-#ifdef CONFIG_USB_OHCI_HCD_SSB +-#include "ohci-ssb.c" +-#define SSB_OHCI_DRIVER ssb_ohci_driver +-#endif +- + #ifdef CONFIG_MFD_SM501 + #include "ohci-sm501.c" + #define SM501_OHCI_DRIVER ohci_hcd_sm501_driver +@@ -1134,8 +1129,7 @@ MODULE_LICENSE ("GPL"); + !defined(SA1111_DRIVER) && \ + !defined(PS3_SYSTEM_BUS_DRIVER) && \ + !defined(SM501_OHCI_DRIVER) && \ +- !defined(TMIO_OHCI_DRIVER) && \ +- !defined(SSB_OHCI_DRIVER) ++ !defined(TMIO_OHCI_DRIVER) + #error "missing bus glue for ohci-hcd" + #endif + +@@ -1201,12 +1195,6 @@ static int __init ohci_hcd_mod_init(void + goto error_pci; + #endif + +-#ifdef SSB_OHCI_DRIVER +- retval = ssb_driver_register(&SSB_OHCI_DRIVER); +- if (retval) +- goto error_ssb; +-#endif +- + #ifdef SM501_OHCI_DRIVER + retval = platform_driver_register(&SM501_OHCI_DRIVER); + if (retval < 0) +@@ -1230,10 +1218,6 @@ static int __init ohci_hcd_mod_init(void + platform_driver_unregister(&SM501_OHCI_DRIVER); + error_sm501: + #endif +-#ifdef SSB_OHCI_DRIVER +- ssb_driver_unregister(&SSB_OHCI_DRIVER); +- error_ssb: +-#endif + #ifdef PCI_DRIVER + pci_unregister_driver(&PCI_DRIVER); + error_pci: +@@ -1281,9 +1265,6 @@ static void __exit ohci_hcd_mod_exit(voi + #ifdef SM501_OHCI_DRIVER + platform_driver_unregister(&SM501_OHCI_DRIVER); + #endif +-#ifdef SSB_OHCI_DRIVER +- ssb_driver_unregister(&SSB_OHCI_DRIVER); +-#endif + #ifdef PCI_DRIVER + pci_unregister_driver(&PCI_DRIVER); + #endif +--- a/drivers/usb/host/ohci-ssb.c ++++ /dev/null +@@ -1,260 +0,0 @@ +-/* +- * Sonics Silicon Backplane +- * Broadcom USB-core OHCI driver +- * +- * Copyright 2007 Michael Buesch <m@bues.ch> +- * +- * Derived from the OHCI-PCI driver +- * Copyright 1999 Roman Weissgaerber +- * Copyright 2000-2002 David Brownell +- * Copyright 1999 Linus Torvalds +- * Copyright 1999 Gregory P. Smith +- * +- * Derived from the USBcore related parts of Broadcom-SB +- * Copyright 2005 Broadcom Corporation +- * +- * Licensed under the GNU/GPL. See COPYING for details. +- */ +-#include <linux/ssb/ssb.h> +- +- +-#define SSB_OHCI_TMSLOW_HOSTMODE (1 << 29) +- +-struct ssb_ohci_device { +- struct ohci_hcd ohci; /* _must_ be at the beginning. */ +- +- u32 enable_flags; +-}; +- +-static inline +-struct ssb_ohci_device *hcd_to_ssb_ohci(struct usb_hcd *hcd) +-{ +- return (struct ssb_ohci_device *)(hcd->hcd_priv); +-} +- +- +-static int ssb_ohci_reset(struct usb_hcd *hcd) +-{ +- struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); +- struct ohci_hcd *ohci = &ohcidev->ohci; +- int err; +- +- ohci_hcd_init(ohci); +- err = ohci_init(ohci); +- +- return err; +-} +- +-static int ssb_ohci_start(struct usb_hcd *hcd) +-{ +- struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); +- struct ohci_hcd *ohci = &ohcidev->ohci; +- int err; +- +- err = ohci_run(ohci); +- if (err < 0) { +- ohci_err(ohci, "can't start\n"); +- ohci_stop(hcd); +- } +- +- return err; +-} +- +-static const struct hc_driver ssb_ohci_hc_driver = { +- .description = "ssb-usb-ohci", +- .product_desc = "SSB OHCI Controller", +- .hcd_priv_size = sizeof(struct ssb_ohci_device), +- +- .irq = ohci_irq, +- .flags = HCD_MEMORY | HCD_USB11, +- +- .reset = ssb_ohci_reset, +- .start = ssb_ohci_start, +- .stop = ohci_stop, +- .shutdown = ohci_shutdown, +- +- .urb_enqueue = ohci_urb_enqueue, +- .urb_dequeue = ohci_urb_dequeue, +- .endpoint_disable = ohci_endpoint_disable, +- +- .get_frame_number = ohci_get_frame, +- +- .hub_status_data = ohci_hub_status_data, +- .hub_control = ohci_hub_control, +-#ifdef CONFIG_PM +- .bus_suspend = ohci_bus_suspend, +- .bus_resume = ohci_bus_resume, +-#endif +- +- .start_port_reset = ohci_start_port_reset, +-}; +- +-static void ssb_ohci_detach(struct ssb_device *dev) +-{ +- struct usb_hcd *hcd = ssb_get_drvdata(dev); +- +- if (hcd->driver->shutdown) +- hcd->driver->shutdown(hcd); +- usb_remove_hcd(hcd); +- iounmap(hcd->regs); +- release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +- usb_put_hcd(hcd); +- ssb_device_disable(dev, 0); +-} +- +-static int ssb_ohci_attach(struct ssb_device *dev) +-{ +- struct ssb_ohci_device *ohcidev; +- struct usb_hcd *hcd; +- int err = -ENOMEM; +- u32 tmp, flags = 0; +- +- if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) || +- dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32))) +- return -EOPNOTSUPP; +- +- if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) { +- /* Put the device into host-mode. */ +- flags |= SSB_OHCI_TMSLOW_HOSTMODE; +- ssb_device_enable(dev, flags); +- } else if (dev->id.coreid == SSB_DEV_USB20_HOST) { +- /* +- * USB 2.0 special considerations: +- * +- * In addition to the standard SSB reset sequence, the Host +- * Control Register must be programmed to bring the USB core +- * and various phy components out of reset. +- */ +- ssb_device_enable(dev, 0); +- ssb_write32(dev, 0x200, 0x7ff); +- +- /* Change Flush control reg */ +- tmp = ssb_read32(dev, 0x400); +- tmp &= ~8; +- ssb_write32(dev, 0x400, tmp); +- tmp = ssb_read32(dev, 0x400); +- +- /* Change Shim control reg */ +- tmp = ssb_read32(dev, 0x304); +- tmp &= ~0x100; +- ssb_write32(dev, 0x304, tmp); +- tmp = ssb_read32(dev, 0x304); +- +- udelay(1); +- +- /* Work around for 5354 failures */ +- if (dev->id.revision == 2 && dev->bus->chip_id == 0x5354) { +- /* Change syn01 reg */ +- tmp = 0x00fe00fe; +- ssb_write32(dev, 0x894, tmp); +- +- /* Change syn03 reg */ +- tmp = ssb_read32(dev, 0x89c); +- tmp |= 0x1; +- ssb_write32(dev, 0x89c, tmp); +- } +- } else +- ssb_device_enable(dev, 0); +- +- hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev, +- dev_name(dev->dev)); +- if (!hcd) +- goto err_dev_disable; +- ohcidev = hcd_to_ssb_ohci(hcd); +- ohcidev->enable_flags = flags; +- +- tmp = ssb_read32(dev, SSB_ADMATCH0); +- hcd->rsrc_start = ssb_admatch_base(tmp); +- hcd->rsrc_len = ssb_admatch_size(tmp); +- hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); +- if (!hcd->regs) +- goto err_put_hcd; +- err = usb_add_hcd(hcd, dev->irq, IRQF_SHARED); +- if (err) +- goto err_iounmap; +- +- ssb_set_drvdata(dev, hcd); +- +- return err; +- +-err_iounmap: +- iounmap(hcd->regs); +-err_put_hcd: +- usb_put_hcd(hcd); +-err_dev_disable: +- ssb_device_disable(dev, flags); +- return err; +-} +- +-static int ssb_ohci_probe(struct ssb_device *dev, +- const struct ssb_device_id *id) +-{ +- int err; +- u16 chipid_top; +- +- /* USBcores are only connected on embedded devices. */ +- chipid_top = (dev->bus->chip_id & 0xFF00); +- if (chipid_top != 0x4700 && chipid_top != 0x5300) +- return -ENODEV; +- +- /* TODO: Probably need checks here; is the core connected? */ +- +- if (usb_disabled()) +- return -ENODEV; +- +- /* We currently always attach SSB_DEV_USB11_HOSTDEV +- * as HOST OHCI. If we want to attach it as Client device, +- * we must branch here and call into the (yet to +- * be written) Client mode driver. Same for remove(). */ +- +- err = ssb_ohci_attach(dev); +- +- return err; +-} +- +-static void ssb_ohci_remove(struct ssb_device *dev) +-{ +- ssb_ohci_detach(dev); +-} +- +-#ifdef CONFIG_PM +- +-static int ssb_ohci_suspend(struct ssb_device *dev, pm_message_t state) +-{ +- ssb_device_disable(dev, 0); +- +- return 0; +-} +- +-static int ssb_ohci_resume(struct ssb_device *dev) +-{ +- struct usb_hcd *hcd = ssb_get_drvdata(dev); +- struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); +- +- ssb_device_enable(dev, ohcidev->enable_flags); +- +- ohci_finish_controller_resume(hcd); +- return 0; +-} +- +-#else /* !CONFIG_PM */ +-#define ssb_ohci_suspend NULL +-#define ssb_ohci_resume NULL +-#endif /* CONFIG_PM */ +- +-static const struct ssb_device_id ssb_ohci_table[] = { +- SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV), +- SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV), +- SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV), +- SSB_DEVTABLE_END +-}; +-MODULE_DEVICE_TABLE(ssb, ssb_ohci_table); +- +-static struct ssb_driver ssb_ohci_driver = { +- .name = KBUILD_MODNAME, +- .id_table = ssb_ohci_table, +- .probe = ssb_ohci_probe, +- .remove = ssb_ohci_remove, +- .suspend = ssb_ohci_suspend, +- .resume = ssb_ohci_resume, +-}; diff --git a/target/linux/brcm47xx/patches-3.3/186-USB-EHCI-bcma-fix-driver.patch b/target/linux/brcm47xx/patches-3.3/186-USB-EHCI-bcma-fix-driver.patch new file mode 100644 index 000000000..b3800340c --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/186-USB-EHCI-bcma-fix-driver.patch @@ -0,0 +1,11 @@ +--- a/drivers/usb/host/bcma-hcd.c ++++ b/drivers/usb/host/bcma-hcd.c +@@ -45,7 +45,7 @@ static int bcma_wait_bits(struct bcma_de + + for (i = 0; i < timeout; i++) { + val = bcma_read32(dev, reg); +- if ((val & bitmask) == bitmask) ++ if ((val & bitmask) == 0) + return 0; + udelay(10); + } diff --git a/target/linux/brcm47xx/patches-3.3/187-USB-EHCI-platform-remove-update.patch b/target/linux/brcm47xx/patches-3.3/187-USB-EHCI-platform-remove-update.patch new file mode 100644 index 000000000..e9a5b7005 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/187-USB-EHCI-platform-remove-update.patch @@ -0,0 +1,11 @@ +--- a/drivers/usb/host/ehci-platform.c ++++ b/drivers/usb/host/ehci-platform.c +@@ -75,8 +75,6 @@ static const struct hc_driver ehci_platf + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + +- .update_device = ehci_update_device, +- + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, + }; + diff --git a/target/linux/brcm47xx/patches-3.3/191-MIPS-BCM47XX-ignore-last-memory-page.patch b/target/linux/brcm47xx/patches-3.3/191-MIPS-BCM47XX-ignore-last-memory-page.patch new file mode 100644 index 000000000..9f7d5dcdb --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/191-MIPS-BCM47XX-ignore-last-memory-page.patch @@ -0,0 +1,33 @@ +--- a/arch/mips/bcm47xx/prom.c ++++ b/arch/mips/bcm47xx/prom.c +@@ -27,6 +27,7 @@ + #include <linux/types.h> + #include <linux/kernel.h> + #include <linux/spinlock.h> ++#include <linux/smp.h> + #include <asm/bootinfo.h> + #include <asm/fw/cfe/cfe_api.h> + #include <asm/fw/cfe/cfe_error.h> +@@ -59,6 +60,7 @@ static __init void prom_init_mem(void) + { + unsigned long mem; + unsigned long max; ++ struct cpuinfo_mips *c = ¤t_cpu_data; + + /* Figure out memory size by finding aliases. + * +@@ -87,6 +89,14 @@ static __init void prom_init_mem(void) + break; + } + ++ /* Ignoring the last page when ddr size is 128M. Cached ++ * accesses to last page is causing the processor to prefetch ++ * using address above 128M stepping out of the ddr address ++ * space. ++ */ ++ if (c->cputype == CPU_74K && (mem == (128 << 20))) ++ mem -= 0x1000; ++ + add_memory_region(0, mem, BOOT_MEM_RAM); + } + diff --git a/target/linux/brcm47xx/patches-3.3/192-MIPS-BCM47XX-improve-memory-size-detection.patch b/target/linux/brcm47xx/patches-3.3/192-MIPS-BCM47XX-improve-memory-size-detection.patch new file mode 100644 index 000000000..285c60c71 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/192-MIPS-BCM47XX-improve-memory-size-detection.patch @@ -0,0 +1,38 @@ +--- a/arch/mips/bcm47xx/prom.c ++++ b/arch/mips/bcm47xx/prom.c +@@ -1,6 +1,7 @@ + /* + * Copyright (C) 2004 Florian Schirmer <jolt@tuxbox.org> + * Copyright (C) 2007 Aurelien Jarno <aurelien@aurel32.net> ++ * Copyright (C) 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the +@@ -60,6 +61,7 @@ static __init void prom_init_mem(void) + { + unsigned long mem; + unsigned long max; ++ unsigned long off; + struct cpuinfo_mips *c = ¤t_cpu_data; + + /* Figure out memory size by finding aliases. +@@ -77,15 +79,15 @@ static __init void prom_init_mem(void) + * max contains the biggest possible address supported by the platform. + * If the method wants to try something above we assume 128MB ram. + */ +- max = ((unsigned long)(prom_init) | ((128 << 20) - 1)); ++ off = (unsigned long)prom_init; ++ max = off | ((128 << 20) - 1); + for (mem = (1 << 20); mem < (128 << 20); mem += (1 << 20)) { +- if (((unsigned long)(prom_init) + mem) > max) { ++ if ((off + mem) > max) { + mem = (128 << 20); + printk(KERN_DEBUG "assume 128MB RAM\n"); + break; + } +- if (*(unsigned long *)((unsigned long)(prom_init) + mem) == +- *(unsigned long *)(prom_init)) ++ if (!memcmp(prom_init, prom_init + mem, 32)) + break; + } + diff --git a/target/linux/brcm47xx/patches-3.3/193-MIPS-BCM47xx-read-out-full-board-data.patch b/target/linux/brcm47xx/patches-3.3/193-MIPS-BCM47xx-read-out-full-board-data.patch new file mode 100644 index 000000000..bdfc348cc --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/193-MIPS-BCM47xx-read-out-full-board-data.patch @@ -0,0 +1,81 @@ +--- a/arch/mips/bcm47xx/sprom.c ++++ b/arch/mips/bcm47xx/sprom.c +@@ -164,10 +164,6 @@ static void nvram_read_alpha2(const char + static void bcm47xx_fill_sprom_r1234589(struct ssb_sprom *sprom, + const char *prefix) + { +- nvram_read_u16(prefix, NULL, "boardrev", &sprom->board_rev, 0); +- if (!sprom->board_rev) +- nvram_read_u16(NULL, NULL, "boardrev", &sprom->board_rev, 0); +- nvram_read_u16(prefix, NULL, "boardnum", &sprom->board_num, 0); + nvram_read_u8(prefix, NULL, "ledbh0", &sprom->gpio0, 0xff); + nvram_read_u8(prefix, NULL, "ledbh1", &sprom->gpio1, 0xff); + nvram_read_u8(prefix, NULL, "ledbh2", &sprom->gpio2, 0xff); +@@ -214,13 +210,6 @@ static void bcm47xx_fill_sprom_r2389(str + nvram_read_u8(prefix, NULL, "pa1himaxpwr", &sprom->maxpwr_ah, 0); + } + +-static void bcm47xx_fill_sprom_r2(struct ssb_sprom *sprom, const char *prefix) +-{ +- nvram_read_u32_2(prefix, "boardflags", &sprom->boardflags_lo, +- &sprom->boardflags_hi); +- nvram_read_u16(prefix, NULL, "boardtype", &sprom->board_type, 0); +-} +- + static void bcm47xx_fill_sprom_r389(struct ssb_sprom *sprom, const char *prefix) + { + nvram_read_u8(prefix, NULL, "bxa2g", &sprom->bxa2g, 0); +@@ -241,9 +230,6 @@ static void bcm47xx_fill_sprom_r389(stru + + static void bcm47xx_fill_sprom_r3(struct ssb_sprom *sprom, const char *prefix) + { +- nvram_read_u32_2(prefix, "boardflags", &sprom->boardflags_lo, +- &sprom->boardflags_hi); +- nvram_read_u16(prefix, NULL, "boardtype", &sprom->board_type, 0); + nvram_read_u8(prefix, NULL, "regrev", &sprom->regrev, 0); + nvram_read_leddc(prefix, "leddc", &sprom->leddc_on_time, + &sprom->leddc_off_time); +@@ -252,11 +238,6 @@ static void bcm47xx_fill_sprom_r3(struct + static void bcm47xx_fill_sprom_r4589(struct ssb_sprom *sprom, + const char *prefix) + { +- nvram_read_u32_2(prefix, "boardflags", &sprom->boardflags_lo, +- &sprom->boardflags_hi); +- nvram_read_u32_2(prefix, "boardflags2", &sprom->boardflags2_lo, +- &sprom->boardflags2_hi); +- nvram_read_u16(prefix, NULL, "boardtype", &sprom->board_type, 0); + nvram_read_u8(prefix, NULL, "regrev", &sprom->regrev, 0); + nvram_read_s8(prefix, NULL, "ag2", &sprom->antenna_gain.a2, 0); + nvram_read_s8(prefix, NULL, "ag3", &sprom->antenna_gain.a3, 0); +@@ -555,9 +536,23 @@ void bcm47xx_fill_sprom_ethernet(struct + nvram_read_macaddr(prefix, "il0macaddr", &sprom->il0mac); + } + ++static void bcm47xx_fill_board_data(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u16(prefix, NULL, "boardrev", &sprom->board_rev, 0); ++ if (!sprom->board_rev) ++ nvram_read_u16(NULL, NULL, "boardrev", &sprom->board_rev, 0); ++ nvram_read_u16(prefix, NULL, "boardnum", &sprom->board_num, 0); ++ nvram_read_u16(prefix, NULL, "boardtype", &sprom->board_type, 0); ++ nvram_read_u32_2(prefix, "boardflags", &sprom->boardflags_lo, ++ &sprom->boardflags_hi); ++ nvram_read_u32_2(prefix, "boardflags2", &sprom->boardflags2_lo, ++ &sprom->boardflags2_hi); ++} ++ + void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix) + { + bcm47xx_fill_sprom_ethernet(sprom, prefix); ++ bcm47xx_fill_board_data(sprom, prefix); + + nvram_read_u8(prefix, NULL, "sromrev", &sprom->revision, 0); + +@@ -571,7 +566,6 @@ void bcm47xx_fill_sprom(struct ssb_sprom + bcm47xx_fill_sprom_r1234589(sprom, prefix); + bcm47xx_fill_sprom_r12389(sprom, prefix); + bcm47xx_fill_sprom_r2389(sprom, prefix); +- bcm47xx_fill_sprom_r2(sprom, prefix); + break; + case 3: + bcm47xx_fill_sprom_r1234589(sprom, prefix); diff --git a/target/linux/brcm47xx/patches-3.3/194-MIPS-BCM47XX-read-sprom-without-prefix-if-no-ieee802.patch b/target/linux/brcm47xx/patches-3.3/194-MIPS-BCM47XX-read-sprom-without-prefix-if-no-ieee802.patch new file mode 100644 index 000000000..69d034cca --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/194-MIPS-BCM47XX-read-sprom-without-prefix-if-no-ieee802.patch @@ -0,0 +1,11 @@ +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -258,6 +258,8 @@ static int bcm47xx_get_sprom_bcma(struct + snprintf(prefix, sizeof(prefix), "sb/%u/", + core->core_index); + bcm47xx_fill_sprom(out, prefix); ++ } else { ++ bcm47xx_fill_sprom(out, NULL); + } + return 0; + default: diff --git a/target/linux/brcm47xx/patches-3.3/195-MIPS-BCM47xx-sprom-read-values-without-prefix-as-fal.patch b/target/linux/brcm47xx/patches-3.3/195-MIPS-BCM47xx-sprom-read-values-without-prefix-as-fal.patch new file mode 100644 index 000000000..c486e68c0 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/195-MIPS-BCM47xx-sprom-read-values-without-prefix-as-fal.patch @@ -0,0 +1,1095 @@ +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -127,7 +127,7 @@ static int bcm47xx_get_sprom_ssb(struct + snprintf(prefix, sizeof(prefix), "pci/%u/%u/", + bus->host_pci->bus->number + 1, + PCI_SLOT(bus->host_pci->devfn)); +- bcm47xx_fill_sprom(out, prefix); ++ bcm47xx_fill_sprom(out, prefix, false); + return 0; + } else { + printk(KERN_WARNING "bcm47xx: unable to fill SPROM for given bustype.\n"); +@@ -146,7 +146,7 @@ static int bcm47xx_get_invariants(struct + bcm47xx_fill_ssb_boardinfo(&iv->boardinfo, NULL); + + memset(&iv->sprom, 0, sizeof(struct ssb_sprom)); +- bcm47xx_fill_sprom(&iv->sprom, NULL); ++ bcm47xx_fill_sprom(&iv->sprom, NULL, false); + + if (nvram_getenv("cardbus", buf, sizeof(buf)) >= 0) + iv->has_cardbus_slot = !!simple_strtoul(buf, NULL, 10); +@@ -248,18 +248,17 @@ static int bcm47xx_get_sprom_bcma(struct + snprintf(prefix, sizeof(prefix), "pci/%u/%u/", + bus->host_pci->bus->number + 1, + PCI_SLOT(bus->host_pci->devfn)); +- bcm47xx_fill_sprom(out, prefix); ++ bcm47xx_fill_sprom(out, prefix, false); + return 0; + case BCMA_HOSTTYPE_SOC: + memset(out, 0, sizeof(struct ssb_sprom)); +- bcm47xx_fill_sprom_ethernet(out, NULL); + core = bcma_find_core(bus, BCMA_CORE_80211); + if (core) { + snprintf(prefix, sizeof(prefix), "sb/%u/", + core->core_index); +- bcm47xx_fill_sprom(out, prefix); ++ bcm47xx_fill_sprom(out, prefix, true); + } else { +- bcm47xx_fill_sprom(out, NULL); ++ bcm47xx_fill_sprom(out, NULL, false); + } + return 0; + default: +--- a/arch/mips/bcm47xx/sprom.c ++++ b/arch/mips/bcm47xx/sprom.c +@@ -42,25 +42,39 @@ static void create_key(const char *prefi + snprintf(buf, len, "%s", name); + } + ++static int get_nvram_var(const char *prefix, const char *postfix, ++ const char *name, char *buf, int len, bool fallback) ++{ ++ char key[40]; ++ int err; ++ ++ create_key(prefix, postfix, name, key, sizeof(key)); ++ ++ err = nvram_getenv(key, buf, len); ++ if (fallback && err == NVRAM_ERR_ENVNOTFOUND && prefix) { ++ create_key(NULL, postfix, name, key, sizeof(key)); ++ err = nvram_getenv(key, buf, len); ++ } ++ return err; ++} ++ + #define NVRAM_READ_VAL(type) \ + static void nvram_read_ ## type (const char *prefix, \ + const char *postfix, const char *name, \ +- type *val, type allset) \ ++ type *val, type allset, bool fallback) \ + { \ + char buf[100]; \ +- char key[40]; \ + int err; \ + type var; \ + \ +- create_key(prefix, postfix, name, key, sizeof(key)); \ +- \ +- err = nvram_getenv(key, buf, sizeof(buf)); \ ++ err = get_nvram_var(prefix, postfix, name, buf, sizeof(buf), \ ++ fallback); \ + if (err < 0) \ + return; \ + err = kstrto ## type (buf, 0, &var); \ + if (err) { \ +- pr_warn("can not parse nvram name %s with value %s" \ +- " got %i", key, buf, err); \ ++ pr_warn("can not parse nvram name %s%s%s with value %s got %i\n", \ ++ prefix, name, postfix, buf, err); \ + return; \ + } \ + if (allset && var == allset) \ +@@ -76,22 +90,19 @@ NVRAM_READ_VAL(u32) + #undef NVRAM_READ_VAL + + static void nvram_read_u32_2(const char *prefix, const char *name, +- u16 *val_lo, u16 *val_hi) ++ u16 *val_lo, u16 *val_hi, bool fallback) + { + char buf[100]; +- char key[40]; + int err; + u32 val; + +- create_key(prefix, NULL, name, key, sizeof(key)); +- +- err = nvram_getenv(key, buf, sizeof(buf)); ++ err = get_nvram_var(prefix, NULL, name, buf, sizeof(buf), fallback); + if (err < 0) + return; + err = kstrtou32(buf, 0, &val); + if (err) { +- pr_warn("can not parse nvram name %s with value %s got %i", +- key, buf, err); ++ pr_warn("can not parse nvram name %s%s with value %s got %i\n", ++ prefix, name, buf, err); + return; + } + *val_lo = (val & 0x0000FFFFU); +@@ -99,22 +110,20 @@ static void nvram_read_u32_2(const char + } + + static void nvram_read_leddc(const char *prefix, const char *name, +- u8 *leddc_on_time, u8 *leddc_off_time) ++ u8 *leddc_on_time, u8 *leddc_off_time, ++ bool fallback) + { + char buf[100]; +- char key[40]; + int err; + u32 val; + +- create_key(prefix, NULL, name, key, sizeof(key)); +- +- err = nvram_getenv(key, buf, sizeof(buf)); ++ err = get_nvram_var(prefix, NULL, name, buf, sizeof(buf), fallback); + if (err < 0) + return; + err = kstrtou32(buf, 0, &val); + if (err) { +- pr_warn("can not parse nvram name %s with value %s got %i", +- key, buf, err); ++ pr_warn("can not parse nvram name %s%s with value %s got %i\n", ++ prefix, name, buf, err); + return; + } + +@@ -126,336 +135,435 @@ static void nvram_read_leddc(const char + } + + static void nvram_read_macaddr(const char *prefix, const char *name, +- u8 (*val)[6]) ++ u8 (*val)[6], bool fallback) + { + char buf[100]; +- char key[40]; + int err; + +- create_key(prefix, NULL, name, key, sizeof(key)); +- +- err = nvram_getenv(key, buf, sizeof(buf)); ++ err = get_nvram_var(prefix, NULL, name, buf, sizeof(buf), fallback); + if (err < 0) + return; ++ + nvram_parse_macaddr(buf, *val); + } + + static void nvram_read_alpha2(const char *prefix, const char *name, +- char (*val)[2]) ++ char (*val)[2], bool fallback) + { + char buf[10]; +- char key[40]; + int err; + +- create_key(prefix, NULL, name, key, sizeof(key)); +- +- err = nvram_getenv(key, buf, sizeof(buf)); ++ err = get_nvram_var(prefix, NULL, name, buf, sizeof(buf), fallback); + if (err < 0) + return; + if (buf[0] == '0') + return; + if (strlen(buf) > 2) { +- pr_warn("alpha2 is too long %s", buf); ++ pr_warn("alpha2 is too long %s\n", buf); + return; + } + memcpy(val, buf, sizeof(val)); + } + + static void bcm47xx_fill_sprom_r1234589(struct ssb_sprom *sprom, +- const char *prefix) ++ const char *prefix, bool fallback) + { +- nvram_read_u8(prefix, NULL, "ledbh0", &sprom->gpio0, 0xff); +- nvram_read_u8(prefix, NULL, "ledbh1", &sprom->gpio1, 0xff); +- nvram_read_u8(prefix, NULL, "ledbh2", &sprom->gpio2, 0xff); +- nvram_read_u8(prefix, NULL, "ledbh3", &sprom->gpio3, 0xff); +- nvram_read_u8(prefix, NULL, "aa2g", &sprom->ant_available_bg, 0); +- nvram_read_u8(prefix, NULL, "aa5g", &sprom->ant_available_a, 0); +- nvram_read_s8(prefix, NULL, "ag0", &sprom->antenna_gain.a0, 0); +- nvram_read_s8(prefix, NULL, "ag1", &sprom->antenna_gain.a1, 0); +- nvram_read_alpha2(prefix, "ccode", &sprom->alpha2); ++ nvram_read_u8(prefix, NULL, "ledbh0", &sprom->gpio0, 0xff, fallback); ++ nvram_read_u8(prefix, NULL, "ledbh1", &sprom->gpio1, 0xff, fallback); ++ nvram_read_u8(prefix, NULL, "ledbh2", &sprom->gpio2, 0xff, fallback); ++ nvram_read_u8(prefix, NULL, "ledbh3", &sprom->gpio3, 0xff, fallback); ++ nvram_read_u8(prefix, NULL, "aa2g", &sprom->ant_available_bg, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "aa5g", &sprom->ant_available_a, 0, ++ fallback); ++ nvram_read_s8(prefix, NULL, "ag0", &sprom->antenna_gain.a0, 0, ++ fallback); ++ nvram_read_s8(prefix, NULL, "ag1", &sprom->antenna_gain.a1, 0, ++ fallback); ++ nvram_read_alpha2(prefix, "ccode", &sprom->alpha2, fallback); + } + + static void bcm47xx_fill_sprom_r12389(struct ssb_sprom *sprom, +- const char *prefix) ++ const char *prefix, bool fallback) + { +- nvram_read_u16(prefix, NULL, "pa0b0", &sprom->pa0b0, 0); +- nvram_read_u16(prefix, NULL, "pa0b1", &sprom->pa0b1, 0); +- nvram_read_u16(prefix, NULL, "pa0b2", &sprom->pa0b2, 0); +- nvram_read_u8(prefix, NULL, "pa0itssit", &sprom->itssi_bg, 0); +- nvram_read_u8(prefix, NULL, "pa0maxpwr", &sprom->maxpwr_bg, 0); +- nvram_read_u16(prefix, NULL, "pa1b0", &sprom->pa1b0, 0); +- nvram_read_u16(prefix, NULL, "pa1b1", &sprom->pa1b1, 0); +- nvram_read_u16(prefix, NULL, "pa1b2", &sprom->pa1b2, 0); +- nvram_read_u8(prefix, NULL, "pa1itssit", &sprom->itssi_a, 0); +- nvram_read_u8(prefix, NULL, "pa1maxpwr", &sprom->maxpwr_a, 0); +-} +- +-static void bcm47xx_fill_sprom_r1(struct ssb_sprom *sprom, const char *prefix) +-{ +- nvram_read_u16(prefix, NULL, "boardflags", &sprom->boardflags_lo, 0); +- nvram_read_u8(prefix, NULL, "cc", &sprom->country_code, 0); ++ nvram_read_u16(prefix, NULL, "pa0b0", &sprom->pa0b0, 0, fallback); ++ nvram_read_u16(prefix, NULL, "pa0b1", &sprom->pa0b1, 0, fallback); ++ nvram_read_u16(prefix, NULL, "pa0b2", &sprom->pa0b2, 0, fallback); ++ nvram_read_u8(prefix, NULL, "pa0itssit", &sprom->itssi_bg, 0, fallback); ++ nvram_read_u8(prefix, NULL, "pa0maxpwr", &sprom->maxpwr_bg, 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "pa1b0", &sprom->pa1b0, 0, fallback); ++ nvram_read_u16(prefix, NULL, "pa1b1", &sprom->pa1b1, 0, fallback); ++ nvram_read_u16(prefix, NULL, "pa1b2", &sprom->pa1b2, 0, fallback); ++ nvram_read_u8(prefix, NULL, "pa1itssit", &sprom->itssi_a, 0, fallback); ++ nvram_read_u8(prefix, NULL, "pa1maxpwr", &sprom->maxpwr_a, 0, fallback); ++} ++ ++static void bcm47xx_fill_sprom_r1(struct ssb_sprom *sprom, const char *prefix, ++ bool fallback) ++{ ++ nvram_read_u16(prefix, NULL, "boardflags", &sprom->boardflags_lo, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "cc", &sprom->country_code, 0, fallback); + } + + static void bcm47xx_fill_sprom_r2389(struct ssb_sprom *sprom, +- const char *prefix) ++ const char *prefix, bool fallback) + { +- nvram_read_u8(prefix, NULL, "opo", &sprom->opo, 0); +- nvram_read_u16(prefix, NULL, "pa1lob0", &sprom->pa1lob0, 0); +- nvram_read_u16(prefix, NULL, "pa1lob1", &sprom->pa1lob1, 0); +- nvram_read_u16(prefix, NULL, "pa1lob2", &sprom->pa1lob2, 0); +- nvram_read_u16(prefix, NULL, "pa1hib0", &sprom->pa1hib0, 0); +- nvram_read_u16(prefix, NULL, "pa1hib1", &sprom->pa1hib1, 0); +- nvram_read_u16(prefix, NULL, "pa1hib2", &sprom->pa1hib2, 0); +- nvram_read_u8(prefix, NULL, "pa1lomaxpwr", &sprom->maxpwr_al, 0); +- nvram_read_u8(prefix, NULL, "pa1himaxpwr", &sprom->maxpwr_ah, 0); +-} +- +-static void bcm47xx_fill_sprom_r389(struct ssb_sprom *sprom, const char *prefix) +-{ +- nvram_read_u8(prefix, NULL, "bxa2g", &sprom->bxa2g, 0); +- nvram_read_u8(prefix, NULL, "rssisav2g", &sprom->rssisav2g, 0); +- nvram_read_u8(prefix, NULL, "rssismc2g", &sprom->rssismc2g, 0); +- nvram_read_u8(prefix, NULL, "rssismf2g", &sprom->rssismf2g, 0); +- nvram_read_u8(prefix, NULL, "bxa5g", &sprom->bxa5g, 0); +- nvram_read_u8(prefix, NULL, "rssisav5g", &sprom->rssisav5g, 0); +- nvram_read_u8(prefix, NULL, "rssismc5g", &sprom->rssismc5g, 0); +- nvram_read_u8(prefix, NULL, "rssismf5g", &sprom->rssismf5g, 0); +- nvram_read_u8(prefix, NULL, "tri2g", &sprom->tri2g, 0); +- nvram_read_u8(prefix, NULL, "tri5g", &sprom->tri5g, 0); +- nvram_read_u8(prefix, NULL, "tri5gl", &sprom->tri5gl, 0); +- nvram_read_u8(prefix, NULL, "tri5gh", &sprom->tri5gh, 0); +- nvram_read_s8(prefix, NULL, "rxpo2g", &sprom->rxpo2g, 0); +- nvram_read_s8(prefix, NULL, "rxpo5g", &sprom->rxpo5g, 0); ++ nvram_read_u8(prefix, NULL, "opo", &sprom->opo, 0, fallback); ++ nvram_read_u16(prefix, NULL, "pa1lob0", &sprom->pa1lob0, 0, fallback); ++ nvram_read_u16(prefix, NULL, "pa1lob1", &sprom->pa1lob1, 0, fallback); ++ nvram_read_u16(prefix, NULL, "pa1lob2", &sprom->pa1lob2, 0, fallback); ++ nvram_read_u16(prefix, NULL, "pa1hib0", &sprom->pa1hib0, 0, fallback); ++ nvram_read_u16(prefix, NULL, "pa1hib1", &sprom->pa1hib1, 0, fallback); ++ nvram_read_u16(prefix, NULL, "pa1hib2", &sprom->pa1hib2, 0, fallback); ++ nvram_read_u8(prefix, NULL, "pa1lomaxpwr", &sprom->maxpwr_al, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "pa1himaxpwr", &sprom->maxpwr_ah, 0, ++ fallback); ++} ++ ++static void bcm47xx_fill_sprom_r389(struct ssb_sprom *sprom, const char *prefix, ++ bool fallback) ++{ ++ nvram_read_u8(prefix, NULL, "bxa2g", &sprom->bxa2g, 0, fallback); ++ nvram_read_u8(prefix, NULL, "rssisav2g", &sprom->rssisav2g, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "rssismc2g", &sprom->rssismc2g, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "rssismf2g", &sprom->rssismf2g, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "bxa5g", &sprom->bxa5g, 0, fallback); ++ nvram_read_u8(prefix, NULL, "rssisav5g", &sprom->rssisav5g, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "rssismc5g", &sprom->rssismc5g, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "rssismf5g", &sprom->rssismf5g, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "tri2g", &sprom->tri2g, 0, fallback); ++ nvram_read_u8(prefix, NULL, "tri5g", &sprom->tri5g, 0, fallback); ++ nvram_read_u8(prefix, NULL, "tri5gl", &sprom->tri5gl, 0, fallback); ++ nvram_read_u8(prefix, NULL, "tri5gh", &sprom->tri5gh, 0, fallback); ++ nvram_read_s8(prefix, NULL, "rxpo2g", &sprom->rxpo2g, 0, fallback); ++ nvram_read_s8(prefix, NULL, "rxpo5g", &sprom->rxpo5g, 0, fallback); + } + +-static void bcm47xx_fill_sprom_r3(struct ssb_sprom *sprom, const char *prefix) ++static void bcm47xx_fill_sprom_r3(struct ssb_sprom *sprom, const char *prefix, ++ bool fallback) + { +- nvram_read_u8(prefix, NULL, "regrev", &sprom->regrev, 0); ++ nvram_read_u8(prefix, NULL, "regrev", &sprom->regrev, 0, fallback); + nvram_read_leddc(prefix, "leddc", &sprom->leddc_on_time, +- &sprom->leddc_off_time); ++ &sprom->leddc_off_time, fallback); + } + + static void bcm47xx_fill_sprom_r4589(struct ssb_sprom *sprom, +- const char *prefix) ++ const char *prefix, bool fallback) + { +- nvram_read_u8(prefix, NULL, "regrev", &sprom->regrev, 0); +- nvram_read_s8(prefix, NULL, "ag2", &sprom->antenna_gain.a2, 0); +- nvram_read_s8(prefix, NULL, "ag3", &sprom->antenna_gain.a3, 0); +- nvram_read_u8(prefix, NULL, "txchain", &sprom->txchain, 0xf); +- nvram_read_u8(prefix, NULL, "rxchain", &sprom->rxchain, 0xf); +- nvram_read_u8(prefix, NULL, "antswitch", &sprom->antswitch, 0xff); ++ nvram_read_u8(prefix, NULL, "regrev", &sprom->regrev, 0, fallback); ++ nvram_read_s8(prefix, NULL, "ag2", &sprom->antenna_gain.a2, 0, ++ fallback); ++ nvram_read_s8(prefix, NULL, "ag3", &sprom->antenna_gain.a3, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "txchain", &sprom->txchain, 0xf, fallback); ++ nvram_read_u8(prefix, NULL, "rxchain", &sprom->rxchain, 0xf, fallback); ++ nvram_read_u8(prefix, NULL, "antswitch", &sprom->antswitch, 0xff, ++ fallback); + nvram_read_leddc(prefix, "leddc", &sprom->leddc_on_time, +- &sprom->leddc_off_time); ++ &sprom->leddc_off_time, fallback); + } + +-static void bcm47xx_fill_sprom_r458(struct ssb_sprom *sprom, const char *prefix) ++static void bcm47xx_fill_sprom_r458(struct ssb_sprom *sprom, const char *prefix, ++ bool fallback) + { +- nvram_read_u16(prefix, NULL, "cck2gpo", &sprom->cck2gpo, 0); +- nvram_read_u32(prefix, NULL, "ofdm2gpo", &sprom->ofdm2gpo, 0); +- nvram_read_u32(prefix, NULL, "ofdm5gpo", &sprom->ofdm5gpo, 0); +- nvram_read_u32(prefix, NULL, "ofdm5glpo", &sprom->ofdm5glpo, 0); +- nvram_read_u32(prefix, NULL, "ofdm5ghpo", &sprom->ofdm5ghpo, 0); +- nvram_read_u16(prefix, NULL, "cddpo", &sprom->cddpo, 0); +- nvram_read_u16(prefix, NULL, "stbcpo", &sprom->stbcpo, 0); +- nvram_read_u16(prefix, NULL, "bw40po", &sprom->bw40po, 0); +- nvram_read_u16(prefix, NULL, "bwduppo", &sprom->bwduppo, 0); +- nvram_read_u16(prefix, NULL, "mcs2gpo0", &sprom->mcs2gpo[0], 0); +- nvram_read_u16(prefix, NULL, "mcs2gpo1", &sprom->mcs2gpo[1], 0); +- nvram_read_u16(prefix, NULL, "mcs2gpo2", &sprom->mcs2gpo[2], 0); +- nvram_read_u16(prefix, NULL, "mcs2gpo3", &sprom->mcs2gpo[3], 0); +- nvram_read_u16(prefix, NULL, "mcs2gpo4", &sprom->mcs2gpo[4], 0); +- nvram_read_u16(prefix, NULL, "mcs2gpo5", &sprom->mcs2gpo[5], 0); +- nvram_read_u16(prefix, NULL, "mcs2gpo6", &sprom->mcs2gpo[6], 0); +- nvram_read_u16(prefix, NULL, "mcs2gpo7", &sprom->mcs2gpo[7], 0); +- nvram_read_u16(prefix, NULL, "mcs5gpo0", &sprom->mcs5gpo[0], 0); +- nvram_read_u16(prefix, NULL, "mcs5gpo1", &sprom->mcs5gpo[1], 0); +- nvram_read_u16(prefix, NULL, "mcs5gpo2", &sprom->mcs5gpo[2], 0); +- nvram_read_u16(prefix, NULL, "mcs5gpo3", &sprom->mcs5gpo[3], 0); +- nvram_read_u16(prefix, NULL, "mcs5gpo4", &sprom->mcs5gpo[4], 0); +- nvram_read_u16(prefix, NULL, "mcs5gpo5", &sprom->mcs5gpo[5], 0); +- nvram_read_u16(prefix, NULL, "mcs5gpo6", &sprom->mcs5gpo[6], 0); +- nvram_read_u16(prefix, NULL, "mcs5gpo7", &sprom->mcs5gpo[7], 0); +- nvram_read_u16(prefix, NULL, "mcs5glpo0", &sprom->mcs5glpo[0], 0); +- nvram_read_u16(prefix, NULL, "mcs5glpo1", &sprom->mcs5glpo[1], 0); +- nvram_read_u16(prefix, NULL, "mcs5glpo2", &sprom->mcs5glpo[2], 0); +- nvram_read_u16(prefix, NULL, "mcs5glpo3", &sprom->mcs5glpo[3], 0); +- nvram_read_u16(prefix, NULL, "mcs5glpo4", &sprom->mcs5glpo[4], 0); +- nvram_read_u16(prefix, NULL, "mcs5glpo5", &sprom->mcs5glpo[5], 0); +- nvram_read_u16(prefix, NULL, "mcs5glpo6", &sprom->mcs5glpo[6], 0); +- nvram_read_u16(prefix, NULL, "mcs5glpo7", &sprom->mcs5glpo[7], 0); +- nvram_read_u16(prefix, NULL, "mcs5ghpo0", &sprom->mcs5ghpo[0], 0); +- nvram_read_u16(prefix, NULL, "mcs5ghpo1", &sprom->mcs5ghpo[1], 0); +- nvram_read_u16(prefix, NULL, "mcs5ghpo2", &sprom->mcs5ghpo[2], 0); +- nvram_read_u16(prefix, NULL, "mcs5ghpo3", &sprom->mcs5ghpo[3], 0); +- nvram_read_u16(prefix, NULL, "mcs5ghpo4", &sprom->mcs5ghpo[4], 0); +- nvram_read_u16(prefix, NULL, "mcs5ghpo5", &sprom->mcs5ghpo[5], 0); +- nvram_read_u16(prefix, NULL, "mcs5ghpo6", &sprom->mcs5ghpo[6], 0); +- nvram_read_u16(prefix, NULL, "mcs5ghpo7", &sprom->mcs5ghpo[7], 0); +-} +- +-static void bcm47xx_fill_sprom_r45(struct ssb_sprom *sprom, const char *prefix) +-{ +- nvram_read_u8(prefix, NULL, "txpid2ga0", &sprom->txpid2g[0], 0); +- nvram_read_u8(prefix, NULL, "txpid2ga1", &sprom->txpid2g[1], 0); +- nvram_read_u8(prefix, NULL, "txpid2ga2", &sprom->txpid2g[2], 0); +- nvram_read_u8(prefix, NULL, "txpid2ga3", &sprom->txpid2g[3], 0); +- nvram_read_u8(prefix, NULL, "txpid5ga0", &sprom->txpid5g[0], 0); +- nvram_read_u8(prefix, NULL, "txpid5ga1", &sprom->txpid5g[1], 0); +- nvram_read_u8(prefix, NULL, "txpid5ga2", &sprom->txpid5g[2], 0); +- nvram_read_u8(prefix, NULL, "txpid5ga3", &sprom->txpid5g[3], 0); +- nvram_read_u8(prefix, NULL, "txpid5gla0", &sprom->txpid5gl[0], 0); +- nvram_read_u8(prefix, NULL, "txpid5gla1", &sprom->txpid5gl[1], 0); +- nvram_read_u8(prefix, NULL, "txpid5gla2", &sprom->txpid5gl[2], 0); +- nvram_read_u8(prefix, NULL, "txpid5gla3", &sprom->txpid5gl[3], 0); +- nvram_read_u8(prefix, NULL, "txpid5gha0", &sprom->txpid5gh[0], 0); +- nvram_read_u8(prefix, NULL, "txpid5gha1", &sprom->txpid5gh[1], 0); +- nvram_read_u8(prefix, NULL, "txpid5gha2", &sprom->txpid5gh[2], 0); +- nvram_read_u8(prefix, NULL, "txpid5gha3", &sprom->txpid5gh[3], 0); ++ nvram_read_u16(prefix, NULL, "cck2gpo", &sprom->cck2gpo, 0, fallback); ++ nvram_read_u32(prefix, NULL, "ofdm2gpo", &sprom->ofdm2gpo, 0, fallback); ++ nvram_read_u32(prefix, NULL, "ofdm5gpo", &sprom->ofdm5gpo, 0, fallback); ++ nvram_read_u32(prefix, NULL, "ofdm5glpo", &sprom->ofdm5glpo, 0, ++ fallback); ++ nvram_read_u32(prefix, NULL, "ofdm5ghpo", &sprom->ofdm5ghpo, 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "cddpo", &sprom->cddpo, 0, fallback); ++ nvram_read_u16(prefix, NULL, "stbcpo", &sprom->stbcpo, 0, fallback); ++ nvram_read_u16(prefix, NULL, "bw40po", &sprom->bw40po, 0, fallback); ++ nvram_read_u16(prefix, NULL, "bwduppo", &sprom->bwduppo, 0, fallback); ++ nvram_read_u16(prefix, NULL, "mcs2gpo0", &sprom->mcs2gpo[0], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs2gpo1", &sprom->mcs2gpo[1], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs2gpo2", &sprom->mcs2gpo[2], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs2gpo3", &sprom->mcs2gpo[3], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs2gpo4", &sprom->mcs2gpo[4], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs2gpo5", &sprom->mcs2gpo[5], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs2gpo6", &sprom->mcs2gpo[6], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs2gpo7", &sprom->mcs2gpo[7], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5gpo0", &sprom->mcs5gpo[0], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5gpo1", &sprom->mcs5gpo[1], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5gpo2", &sprom->mcs5gpo[2], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5gpo3", &sprom->mcs5gpo[3], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5gpo4", &sprom->mcs5gpo[4], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5gpo5", &sprom->mcs5gpo[5], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5gpo6", &sprom->mcs5gpo[6], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5gpo7", &sprom->mcs5gpo[7], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5glpo0", &sprom->mcs5glpo[0], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5glpo1", &sprom->mcs5glpo[1], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5glpo2", &sprom->mcs5glpo[2], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5glpo3", &sprom->mcs5glpo[3], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5glpo4", &sprom->mcs5glpo[4], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5glpo5", &sprom->mcs5glpo[5], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5glpo6", &sprom->mcs5glpo[6], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5glpo7", &sprom->mcs5glpo[7], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo0", &sprom->mcs5ghpo[0], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo1", &sprom->mcs5ghpo[1], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo2", &sprom->mcs5ghpo[2], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo3", &sprom->mcs5ghpo[3], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo4", &sprom->mcs5ghpo[4], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo5", &sprom->mcs5ghpo[5], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo6", &sprom->mcs5ghpo[6], 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo7", &sprom->mcs5ghpo[7], 0, ++ fallback); ++} ++ ++static void bcm47xx_fill_sprom_r45(struct ssb_sprom *sprom, const char *prefix, ++ bool fallback) ++{ ++ nvram_read_u8(prefix, NULL, "txpid2ga0", &sprom->txpid2g[0], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "txpid2ga1", &sprom->txpid2g[1], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "txpid2ga2", &sprom->txpid2g[2], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "txpid2ga3", &sprom->txpid2g[3], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "txpid5ga0", &sprom->txpid5g[0], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "txpid5ga1", &sprom->txpid5g[1], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "txpid5ga2", &sprom->txpid5g[2], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "txpid5ga3", &sprom->txpid5g[3], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "txpid5gla0", &sprom->txpid5gl[0], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "txpid5gla1", &sprom->txpid5gl[1], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "txpid5gla2", &sprom->txpid5gl[2], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "txpid5gla3", &sprom->txpid5gl[3], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "txpid5gha0", &sprom->txpid5gh[0], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "txpid5gha1", &sprom->txpid5gh[1], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "txpid5gha2", &sprom->txpid5gh[2], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "txpid5gha3", &sprom->txpid5gh[3], 0, ++ fallback); + } + +-static void bcm47xx_fill_sprom_r89(struct ssb_sprom *sprom, const char *prefix) ++static void bcm47xx_fill_sprom_r89(struct ssb_sprom *sprom, const char *prefix, ++ bool fallback) + { +- nvram_read_u8(prefix, NULL, "tssipos2g", &sprom->fem.ghz2.tssipos, 0); ++ nvram_read_u8(prefix, NULL, "tssipos2g", &sprom->fem.ghz2.tssipos, 0, ++ fallback); + nvram_read_u8(prefix, NULL, "extpagain2g", +- &sprom->fem.ghz2.extpa_gain, 0); ++ &sprom->fem.ghz2.extpa_gain, 0, fallback); + nvram_read_u8(prefix, NULL, "pdetrange2g", +- &sprom->fem.ghz2.pdet_range, 0); +- nvram_read_u8(prefix, NULL, "triso2g", &sprom->fem.ghz2.tr_iso, 0); +- nvram_read_u8(prefix, NULL, "antswctl2g", &sprom->fem.ghz2.antswlut, 0); +- nvram_read_u8(prefix, NULL, "tssipos5g", &sprom->fem.ghz5.tssipos, 0); ++ &sprom->fem.ghz2.pdet_range, 0, fallback); ++ nvram_read_u8(prefix, NULL, "triso2g", &sprom->fem.ghz2.tr_iso, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "antswctl2g", &sprom->fem.ghz2.antswlut, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "tssipos5g", &sprom->fem.ghz5.tssipos, 0, ++ fallback); + nvram_read_u8(prefix, NULL, "extpagain5g", +- &sprom->fem.ghz5.extpa_gain, 0); ++ &sprom->fem.ghz5.extpa_gain, 0, fallback); + nvram_read_u8(prefix, NULL, "pdetrange5g", +- &sprom->fem.ghz5.pdet_range, 0); +- nvram_read_u8(prefix, NULL, "triso5g", &sprom->fem.ghz5.tr_iso, 0); +- nvram_read_u8(prefix, NULL, "antswctl5g", &sprom->fem.ghz5.antswlut, 0); +- nvram_read_u8(prefix, NULL, "tempthresh", &sprom->tempthresh, 0); +- nvram_read_u8(prefix, NULL, "tempoffset", &sprom->tempoffset, 0); +- nvram_read_u16(prefix, NULL, "rawtempsense", &sprom->rawtempsense, 0); +- nvram_read_u8(prefix, NULL, "measpower", &sprom->measpower, 0); ++ &sprom->fem.ghz5.pdet_range, 0, fallback); ++ nvram_read_u8(prefix, NULL, "triso5g", &sprom->fem.ghz5.tr_iso, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "antswctl5g", &sprom->fem.ghz5.antswlut, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "tempthresh", &sprom->tempthresh, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "tempoffset", &sprom->tempoffset, 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "rawtempsense", &sprom->rawtempsense, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "measpower", &sprom->measpower, 0, ++ fallback); + nvram_read_u8(prefix, NULL, "tempsense_slope", +- &sprom->tempsense_slope, 0); +- nvram_read_u8(prefix, NULL, "tempcorrx", &sprom->tempcorrx, 0); ++ &sprom->tempsense_slope, 0, fallback); ++ nvram_read_u8(prefix, NULL, "tempcorrx", &sprom->tempcorrx, 0, ++ fallback); + nvram_read_u8(prefix, NULL, "tempsense_option", +- &sprom->tempsense_option, 0); ++ &sprom->tempsense_option, 0, fallback); + nvram_read_u8(prefix, NULL, "freqoffset_corr", +- &sprom->freqoffset_corr, 0); +- nvram_read_u8(prefix, NULL, "iqcal_swp_dis", &sprom->iqcal_swp_dis, 0); +- nvram_read_u8(prefix, NULL, "hw_iqcal_en", &sprom->hw_iqcal_en, 0); +- nvram_read_u8(prefix, NULL, "elna2g", &sprom->elna2g, 0); +- nvram_read_u8(prefix, NULL, "elna5g", &sprom->elna5g, 0); ++ &sprom->freqoffset_corr, 0, fallback); ++ nvram_read_u8(prefix, NULL, "iqcal_swp_dis", &sprom->iqcal_swp_dis, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "hw_iqcal_en", &sprom->hw_iqcal_en, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "elna2g", &sprom->elna2g, 0, fallback); ++ nvram_read_u8(prefix, NULL, "elna5g", &sprom->elna5g, 0, fallback); + nvram_read_u8(prefix, NULL, "phycal_tempdelta", +- &sprom->phycal_tempdelta, 0); +- nvram_read_u8(prefix, NULL, "temps_period", &sprom->temps_period, 0); ++ &sprom->phycal_tempdelta, 0, fallback); ++ nvram_read_u8(prefix, NULL, "temps_period", &sprom->temps_period, 0, ++ fallback); + nvram_read_u8(prefix, NULL, "temps_hysteresis", +- &sprom->temps_hysteresis, 0); +- nvram_read_u8(prefix, NULL, "measpower1", &sprom->measpower1, 0); +- nvram_read_u8(prefix, NULL, "measpower2", &sprom->measpower2, 0); ++ &sprom->temps_hysteresis, 0, fallback); ++ nvram_read_u8(prefix, NULL, "measpower1", &sprom->measpower1, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "measpower2", &sprom->measpower2, 0, ++ fallback); + nvram_read_u8(prefix, NULL, "rxgainerr2ga0", +- &sprom->rxgainerr2ga[0], 0); ++ &sprom->rxgainerr2ga[0], 0, fallback); + nvram_read_u8(prefix, NULL, "rxgainerr2ga1", +- &sprom->rxgainerr2ga[1], 0); ++ &sprom->rxgainerr2ga[1], 0, fallback); + nvram_read_u8(prefix, NULL, "rxgainerr2ga2", +- &sprom->rxgainerr2ga[2], 0); ++ &sprom->rxgainerr2ga[2], 0, fallback); + nvram_read_u8(prefix, NULL, "rxgainerr5gla0", +- &sprom->rxgainerr5gla[0], 0); ++ &sprom->rxgainerr5gla[0], 0, fallback); + nvram_read_u8(prefix, NULL, "rxgainerr5gla1", +- &sprom->rxgainerr5gla[1], 0); ++ &sprom->rxgainerr5gla[1], 0, fallback); + nvram_read_u8(prefix, NULL, "rxgainerr5gla2", +- &sprom->rxgainerr5gla[2], 0); ++ &sprom->rxgainerr5gla[2], 0, fallback); + nvram_read_u8(prefix, NULL, "rxgainerr5gma0", +- &sprom->rxgainerr5gma[0], 0); ++ &sprom->rxgainerr5gma[0], 0, fallback); + nvram_read_u8(prefix, NULL, "rxgainerr5gma1", +- &sprom->rxgainerr5gma[1], 0); ++ &sprom->rxgainerr5gma[1], 0, fallback); + nvram_read_u8(prefix, NULL, "rxgainerr5gma2", +- &sprom->rxgainerr5gma[2], 0); ++ &sprom->rxgainerr5gma[2], 0, fallback); + nvram_read_u8(prefix, NULL, "rxgainerr5gha0", +- &sprom->rxgainerr5gha[0], 0); ++ &sprom->rxgainerr5gha[0], 0, fallback); + nvram_read_u8(prefix, NULL, "rxgainerr5gha1", +- &sprom->rxgainerr5gha[1], 0); ++ &sprom->rxgainerr5gha[1], 0, fallback); + nvram_read_u8(prefix, NULL, "rxgainerr5gha2", +- &sprom->rxgainerr5gha[2], 0); ++ &sprom->rxgainerr5gha[2], 0, fallback); + nvram_read_u8(prefix, NULL, "rxgainerr5gua0", +- &sprom->rxgainerr5gua[0], 0); ++ &sprom->rxgainerr5gua[0], 0, fallback); + nvram_read_u8(prefix, NULL, "rxgainerr5gua1", +- &sprom->rxgainerr5gua[1], 0); ++ &sprom->rxgainerr5gua[1], 0, fallback); + nvram_read_u8(prefix, NULL, "rxgainerr5gua2", +- &sprom->rxgainerr5gua[2], 0); +- nvram_read_u8(prefix, NULL, "noiselvl2ga0", &sprom->noiselvl2ga[0], 0); +- nvram_read_u8(prefix, NULL, "noiselvl2ga1", &sprom->noiselvl2ga[1], 0); +- nvram_read_u8(prefix, NULL, "noiselvl2ga2", &sprom->noiselvl2ga[2], 0); ++ &sprom->rxgainerr5gua[2], 0, fallback); ++ nvram_read_u8(prefix, NULL, "noiselvl2ga0", &sprom->noiselvl2ga[0], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "noiselvl2ga1", &sprom->noiselvl2ga[1], 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "noiselvl2ga2", &sprom->noiselvl2ga[2], 0, ++ fallback); + nvram_read_u8(prefix, NULL, "noiselvl5gla0", +- &sprom->noiselvl5gla[0], 0); ++ &sprom->noiselvl5gla[0], 0, fallback); + nvram_read_u8(prefix, NULL, "noiselvl5gla1", +- &sprom->noiselvl5gla[1], 0); ++ &sprom->noiselvl5gla[1], 0, fallback); + nvram_read_u8(prefix, NULL, "noiselvl5gla2", +- &sprom->noiselvl5gla[2], 0); ++ &sprom->noiselvl5gla[2], 0, fallback); + nvram_read_u8(prefix, NULL, "noiselvl5gma0", +- &sprom->noiselvl5gma[0], 0); ++ &sprom->noiselvl5gma[0], 0, fallback); + nvram_read_u8(prefix, NULL, "noiselvl5gma1", +- &sprom->noiselvl5gma[1], 0); ++ &sprom->noiselvl5gma[1], 0, fallback); + nvram_read_u8(prefix, NULL, "noiselvl5gma2", +- &sprom->noiselvl5gma[2], 0); ++ &sprom->noiselvl5gma[2], 0, fallback); + nvram_read_u8(prefix, NULL, "noiselvl5gha0", +- &sprom->noiselvl5gha[0], 0); ++ &sprom->noiselvl5gha[0], 0, fallback); + nvram_read_u8(prefix, NULL, "noiselvl5gha1", +- &sprom->noiselvl5gha[1], 0); ++ &sprom->noiselvl5gha[1], 0, fallback); + nvram_read_u8(prefix, NULL, "noiselvl5gha2", +- &sprom->noiselvl5gha[2], 0); ++ &sprom->noiselvl5gha[2], 0, fallback); + nvram_read_u8(prefix, NULL, "noiselvl5gua0", +- &sprom->noiselvl5gua[0], 0); ++ &sprom->noiselvl5gua[0], 0, fallback); + nvram_read_u8(prefix, NULL, "noiselvl5gua1", +- &sprom->noiselvl5gua[1], 0); ++ &sprom->noiselvl5gua[1], 0, fallback); + nvram_read_u8(prefix, NULL, "noiselvl5gua2", +- &sprom->noiselvl5gua[2], 0); ++ &sprom->noiselvl5gua[2], 0, fallback); + nvram_read_u8(prefix, NULL, "pcieingress_war", +- &sprom->pcieingress_war, 0); ++ &sprom->pcieingress_war, 0, fallback); + } + +-static void bcm47xx_fill_sprom_r9(struct ssb_sprom *sprom, const char *prefix) ++static void bcm47xx_fill_sprom_r9(struct ssb_sprom *sprom, const char *prefix, ++ bool fallback) + { +- nvram_read_u16(prefix, NULL, "cckbw202gpo", &sprom->cckbw202gpo, 0); +- nvram_read_u16(prefix, NULL, "cckbw20ul2gpo", &sprom->cckbw20ul2gpo, 0); ++ nvram_read_u16(prefix, NULL, "cckbw202gpo", &sprom->cckbw202gpo, 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "cckbw20ul2gpo", &sprom->cckbw20ul2gpo, 0, ++ fallback); + nvram_read_u32(prefix, NULL, "legofdmbw202gpo", +- &sprom->legofdmbw202gpo, 0); ++ &sprom->legofdmbw202gpo, 0, fallback); + nvram_read_u32(prefix, NULL, "legofdmbw20ul2gpo", +- &sprom->legofdmbw20ul2gpo, 0); ++ &sprom->legofdmbw20ul2gpo, 0, fallback); + nvram_read_u32(prefix, NULL, "legofdmbw205glpo", +- &sprom->legofdmbw205glpo, 0); ++ &sprom->legofdmbw205glpo, 0, fallback); + nvram_read_u32(prefix, NULL, "legofdmbw20ul5glpo", +- &sprom->legofdmbw20ul5glpo, 0); ++ &sprom->legofdmbw20ul5glpo, 0, fallback); + nvram_read_u32(prefix, NULL, "legofdmbw205gmpo", +- &sprom->legofdmbw205gmpo, 0); ++ &sprom->legofdmbw205gmpo, 0, fallback); + nvram_read_u32(prefix, NULL, "legofdmbw20ul5gmpo", +- &sprom->legofdmbw20ul5gmpo, 0); ++ &sprom->legofdmbw20ul5gmpo, 0, fallback); + nvram_read_u32(prefix, NULL, "legofdmbw205ghpo", +- &sprom->legofdmbw205ghpo, 0); ++ &sprom->legofdmbw205ghpo, 0, fallback); + nvram_read_u32(prefix, NULL, "legofdmbw20ul5ghpo", +- &sprom->legofdmbw20ul5ghpo, 0); +- nvram_read_u32(prefix, NULL, "mcsbw202gpo", &sprom->mcsbw202gpo, 0); +- nvram_read_u32(prefix, NULL, "mcsbw20ul2gpo", &sprom->mcsbw20ul2gpo, 0); +- nvram_read_u32(prefix, NULL, "mcsbw402gpo", &sprom->mcsbw402gpo, 0); +- nvram_read_u32(prefix, NULL, "mcsbw205glpo", &sprom->mcsbw205glpo, 0); ++ &sprom->legofdmbw20ul5ghpo, 0, fallback); ++ nvram_read_u32(prefix, NULL, "mcsbw202gpo", &sprom->mcsbw202gpo, 0, ++ fallback); ++ nvram_read_u32(prefix, NULL, "mcsbw20ul2gpo", &sprom->mcsbw20ul2gpo, 0, ++ fallback); ++ nvram_read_u32(prefix, NULL, "mcsbw402gpo", &sprom->mcsbw402gpo, 0, ++ fallback); ++ nvram_read_u32(prefix, NULL, "mcsbw205glpo", &sprom->mcsbw205glpo, 0, ++ fallback); + nvram_read_u32(prefix, NULL, "mcsbw20ul5glpo", +- &sprom->mcsbw20ul5glpo, 0); +- nvram_read_u32(prefix, NULL, "mcsbw405glpo", &sprom->mcsbw405glpo, 0); +- nvram_read_u32(prefix, NULL, "mcsbw205gmpo", &sprom->mcsbw205gmpo, 0); ++ &sprom->mcsbw20ul5glpo, 0, fallback); ++ nvram_read_u32(prefix, NULL, "mcsbw405glpo", &sprom->mcsbw405glpo, 0, ++ fallback); ++ nvram_read_u32(prefix, NULL, "mcsbw205gmpo", &sprom->mcsbw205gmpo, 0, ++ fallback); + nvram_read_u32(prefix, NULL, "mcsbw20ul5gmpo", +- &sprom->mcsbw20ul5gmpo, 0); +- nvram_read_u32(prefix, NULL, "mcsbw405gmpo", &sprom->mcsbw405gmpo, 0); +- nvram_read_u32(prefix, NULL, "mcsbw205ghpo", &sprom->mcsbw205ghpo, 0); ++ &sprom->mcsbw20ul5gmpo, 0, fallback); ++ nvram_read_u32(prefix, NULL, "mcsbw405gmpo", &sprom->mcsbw405gmpo, 0, ++ fallback); ++ nvram_read_u32(prefix, NULL, "mcsbw205ghpo", &sprom->mcsbw205ghpo, 0, ++ fallback); + nvram_read_u32(prefix, NULL, "mcsbw20ul5ghpo", +- &sprom->mcsbw20ul5ghpo, 0); +- nvram_read_u32(prefix, NULL, "mcsbw405ghpo", &sprom->mcsbw405ghpo, 0); +- nvram_read_u16(prefix, NULL, "mcs32po", &sprom->mcs32po, 0); ++ &sprom->mcsbw20ul5ghpo, 0, fallback); ++ nvram_read_u32(prefix, NULL, "mcsbw405ghpo", &sprom->mcsbw405ghpo, 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "mcs32po", &sprom->mcs32po, 0, fallback); + nvram_read_u16(prefix, NULL, "legofdm40duppo", +- &sprom->legofdm40duppo, 0); +- nvram_read_u8(prefix, NULL, "sar2g", &sprom->sar2g, 0); +- nvram_read_u8(prefix, NULL, "sar5g", &sprom->sar5g, 0); ++ &sprom->legofdm40duppo, 0, fallback); ++ nvram_read_u8(prefix, NULL, "sar2g", &sprom->sar2g, 0, fallback); ++ nvram_read_u8(prefix, NULL, "sar5g", &sprom->sar5g, 0, fallback); + } + + static void bcm47xx_fill_sprom_path_r4589(struct ssb_sprom *sprom, +- const char *prefix) ++ const char *prefix, bool fallback) + { + char postfix[2]; + int i; +@@ -464,46 +572,46 @@ static void bcm47xx_fill_sprom_path_r458 + struct ssb_sprom_core_pwr_info *pwr_info = &sprom->core_pwr_info[i]; + snprintf(postfix, sizeof(postfix), "%i", i); + nvram_read_u8(prefix, postfix, "maxp2ga", +- &pwr_info->maxpwr_2g, 0); ++ &pwr_info->maxpwr_2g, 0, fallback); + nvram_read_u8(prefix, postfix, "itt2ga", +- &pwr_info->itssi_2g, 0); ++ &pwr_info->itssi_2g, 0, fallback); + nvram_read_u8(prefix, postfix, "itt5ga", +- &pwr_info->itssi_5g, 0); ++ &pwr_info->itssi_5g, 0, fallback); + nvram_read_u16(prefix, postfix, "pa2gw0a", +- &pwr_info->pa_2g[0], 0); ++ &pwr_info->pa_2g[0], 0, fallback); + nvram_read_u16(prefix, postfix, "pa2gw1a", +- &pwr_info->pa_2g[1], 0); ++ &pwr_info->pa_2g[1], 0, fallback); + nvram_read_u16(prefix, postfix, "pa2gw2a", +- &pwr_info->pa_2g[2], 0); ++ &pwr_info->pa_2g[2], 0, fallback); + nvram_read_u8(prefix, postfix, "maxp5ga", +- &pwr_info->maxpwr_5g, 0); ++ &pwr_info->maxpwr_5g, 0, fallback); + nvram_read_u8(prefix, postfix, "maxp5gha", +- &pwr_info->maxpwr_5gh, 0); ++ &pwr_info->maxpwr_5gh, 0, fallback); + nvram_read_u8(prefix, postfix, "maxp5gla", +- &pwr_info->maxpwr_5gl, 0); ++ &pwr_info->maxpwr_5gl, 0, fallback); + nvram_read_u16(prefix, postfix, "pa5gw0a", +- &pwr_info->pa_5g[0], 0); ++ &pwr_info->pa_5g[0], 0, fallback); + nvram_read_u16(prefix, postfix, "pa5gw1a", +- &pwr_info->pa_5g[1], 0); ++ &pwr_info->pa_5g[1], 0, fallback); + nvram_read_u16(prefix, postfix, "pa5gw2a", +- &pwr_info->pa_5g[2], 0); ++ &pwr_info->pa_5g[2], 0, fallback); + nvram_read_u16(prefix, postfix, "pa5glw0a", +- &pwr_info->pa_5gl[0], 0); ++ &pwr_info->pa_5gl[0], 0, fallback); + nvram_read_u16(prefix, postfix, "pa5glw1a", +- &pwr_info->pa_5gl[1], 0); ++ &pwr_info->pa_5gl[1], 0, fallback); + nvram_read_u16(prefix, postfix, "pa5glw2a", +- &pwr_info->pa_5gl[2], 0); ++ &pwr_info->pa_5gl[2], 0, fallback); + nvram_read_u16(prefix, postfix, "pa5ghw0a", +- &pwr_info->pa_5gh[0], 0); ++ &pwr_info->pa_5gh[0], 0, fallback); + nvram_read_u16(prefix, postfix, "pa5ghw1a", +- &pwr_info->pa_5gh[1], 0); ++ &pwr_info->pa_5gh[1], 0, fallback); + nvram_read_u16(prefix, postfix, "pa5ghw2a", +- &pwr_info->pa_5gh[2], 0); ++ &pwr_info->pa_5gh[2], 0, fallback); + } + } + + static void bcm47xx_fill_sprom_path_r45(struct ssb_sprom *sprom, +- const char *prefix) ++ const char *prefix, bool fallback) + { + char postfix[2]; + int i; +@@ -512,104 +620,112 @@ static void bcm47xx_fill_sprom_path_r45( + struct ssb_sprom_core_pwr_info *pwr_info = &sprom->core_pwr_info[i]; + snprintf(postfix, sizeof(postfix), "%i", i); + nvram_read_u16(prefix, postfix, "pa2gw3a", +- &pwr_info->pa_2g[3], 0); ++ &pwr_info->pa_2g[3], 0, fallback); + nvram_read_u16(prefix, postfix, "pa5gw3a", +- &pwr_info->pa_5g[3], 0); ++ &pwr_info->pa_5g[3], 0, fallback); + nvram_read_u16(prefix, postfix, "pa5glw3a", +- &pwr_info->pa_5gl[3], 0); ++ &pwr_info->pa_5gl[3], 0, fallback); + nvram_read_u16(prefix, postfix, "pa5ghw3a", +- &pwr_info->pa_5gh[3], 0); ++ &pwr_info->pa_5gh[3], 0, fallback); + } + } + +-void bcm47xx_fill_sprom_ethernet(struct ssb_sprom *sprom, const char *prefix) +-{ +- nvram_read_macaddr(prefix, "et0macaddr", &sprom->et0mac); +- nvram_read_u8(prefix, NULL, "et0mdcport", &sprom->et0mdcport, 0); +- nvram_read_u8(prefix, NULL, "et0phyaddr", &sprom->et0phyaddr, 0); +- +- nvram_read_macaddr(prefix, "et1macaddr", &sprom->et1mac); +- nvram_read_u8(prefix, NULL, "et1mdcport", &sprom->et1mdcport, 0); +- nvram_read_u8(prefix, NULL, "et1phyaddr", &sprom->et1phyaddr, 0); +- +- nvram_read_macaddr(prefix, "macaddr", &sprom->il0mac); +- nvram_read_macaddr(prefix, "il0macaddr", &sprom->il0mac); +-} +- +-static void bcm47xx_fill_board_data(struct ssb_sprom *sprom, const char *prefix) ++static void bcm47xx_fill_sprom_ethernet(struct ssb_sprom *sprom, ++ const char *prefix, bool fallback) + { +- nvram_read_u16(prefix, NULL, "boardrev", &sprom->board_rev, 0); +- if (!sprom->board_rev) +- nvram_read_u16(NULL, NULL, "boardrev", &sprom->board_rev, 0); +- nvram_read_u16(prefix, NULL, "boardnum", &sprom->board_num, 0); +- nvram_read_u16(prefix, NULL, "boardtype", &sprom->board_type, 0); ++ nvram_read_macaddr(prefix, "et0macaddr", &sprom->et0mac, fallback); ++ nvram_read_u8(prefix, NULL, "et0mdcport", &sprom->et0mdcport, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "et0phyaddr", &sprom->et0phyaddr, 0, ++ fallback); ++ ++ nvram_read_macaddr(prefix, "et1macaddr", &sprom->et1mac, fallback); ++ nvram_read_u8(prefix, NULL, "et1mdcport", &sprom->et1mdcport, 0, ++ fallback); ++ nvram_read_u8(prefix, NULL, "et1phyaddr", &sprom->et1phyaddr, 0, ++ fallback); ++ ++ nvram_read_macaddr(prefix, "macaddr", &sprom->il0mac, fallback); ++ nvram_read_macaddr(prefix, "il0macaddr", &sprom->il0mac, fallback); ++} ++ ++static void bcm47xx_fill_board_data(struct ssb_sprom *sprom, const char *prefix, ++ bool fallback) ++{ ++ nvram_read_u16(prefix, NULL, "boardrev", &sprom->board_rev, 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "boardnum", &sprom->board_num, 0, ++ fallback); ++ nvram_read_u16(prefix, NULL, "boardtype", &sprom->board_type, 0, ++ fallback); + nvram_read_u32_2(prefix, "boardflags", &sprom->boardflags_lo, +- &sprom->boardflags_hi); ++ &sprom->boardflags_hi, fallback); + nvram_read_u32_2(prefix, "boardflags2", &sprom->boardflags2_lo, +- &sprom->boardflags2_hi); ++ &sprom->boardflags2_hi, fallback); + } + +-void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix) ++void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix, ++ bool fallback) + { +- bcm47xx_fill_sprom_ethernet(sprom, prefix); +- bcm47xx_fill_board_data(sprom, prefix); ++ bcm47xx_fill_sprom_ethernet(sprom, prefix, fallback); ++ bcm47xx_fill_board_data(sprom, prefix, fallback); + +- nvram_read_u8(prefix, NULL, "sromrev", &sprom->revision, 0); ++ nvram_read_u8(prefix, NULL, "sromrev", &sprom->revision, 0, fallback); + + switch (sprom->revision) { + case 1: +- bcm47xx_fill_sprom_r1234589(sprom, prefix); +- bcm47xx_fill_sprom_r12389(sprom, prefix); +- bcm47xx_fill_sprom_r1(sprom, prefix); ++ bcm47xx_fill_sprom_r1234589(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r12389(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r1(sprom, prefix, fallback); + break; + case 2: +- bcm47xx_fill_sprom_r1234589(sprom, prefix); +- bcm47xx_fill_sprom_r12389(sprom, prefix); +- bcm47xx_fill_sprom_r2389(sprom, prefix); ++ bcm47xx_fill_sprom_r1234589(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r12389(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r2389(sprom, prefix, fallback); + break; + case 3: +- bcm47xx_fill_sprom_r1234589(sprom, prefix); +- bcm47xx_fill_sprom_r12389(sprom, prefix); +- bcm47xx_fill_sprom_r2389(sprom, prefix); +- bcm47xx_fill_sprom_r389(sprom, prefix); +- bcm47xx_fill_sprom_r3(sprom, prefix); ++ bcm47xx_fill_sprom_r1234589(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r12389(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r2389(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r389(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r3(sprom, prefix, fallback); + break; + case 4: + case 5: +- bcm47xx_fill_sprom_r1234589(sprom, prefix); +- bcm47xx_fill_sprom_r4589(sprom, prefix); +- bcm47xx_fill_sprom_r458(sprom, prefix); +- bcm47xx_fill_sprom_r45(sprom, prefix); +- bcm47xx_fill_sprom_path_r4589(sprom, prefix); +- bcm47xx_fill_sprom_path_r45(sprom, prefix); ++ bcm47xx_fill_sprom_r1234589(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r4589(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r458(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r45(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_path_r4589(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_path_r45(sprom, prefix, fallback); + break; + case 8: +- bcm47xx_fill_sprom_r1234589(sprom, prefix); +- bcm47xx_fill_sprom_r12389(sprom, prefix); +- bcm47xx_fill_sprom_r2389(sprom, prefix); +- bcm47xx_fill_sprom_r389(sprom, prefix); +- bcm47xx_fill_sprom_r4589(sprom, prefix); +- bcm47xx_fill_sprom_r458(sprom, prefix); +- bcm47xx_fill_sprom_r89(sprom, prefix); +- bcm47xx_fill_sprom_path_r4589(sprom, prefix); ++ bcm47xx_fill_sprom_r1234589(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r12389(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r2389(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r389(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r4589(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r458(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r89(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_path_r4589(sprom, prefix, fallback); + break; + case 9: +- bcm47xx_fill_sprom_r1234589(sprom, prefix); +- bcm47xx_fill_sprom_r12389(sprom, prefix); +- bcm47xx_fill_sprom_r2389(sprom, prefix); +- bcm47xx_fill_sprom_r389(sprom, prefix); +- bcm47xx_fill_sprom_r4589(sprom, prefix); +- bcm47xx_fill_sprom_r89(sprom, prefix); +- bcm47xx_fill_sprom_r9(sprom, prefix); +- bcm47xx_fill_sprom_path_r4589(sprom, prefix); ++ bcm47xx_fill_sprom_r1234589(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r12389(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r2389(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r389(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r4589(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r89(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r9(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_path_r4589(sprom, prefix, fallback); + break; + default: + pr_warn("Unsupported SPROM revision %d detected. Will extract" + " v1\n", sprom->revision); + sprom->revision = 1; +- bcm47xx_fill_sprom_r1234589(sprom, prefix); +- bcm47xx_fill_sprom_r12389(sprom, prefix); +- bcm47xx_fill_sprom_r1(sprom, prefix); ++ bcm47xx_fill_sprom_r1234589(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r12389(sprom, prefix, fallback); ++ bcm47xx_fill_sprom_r1(sprom, prefix, fallback); + } + } + +@@ -617,11 +733,12 @@ void bcm47xx_fill_sprom(struct ssb_sprom + void bcm47xx_fill_ssb_boardinfo(struct ssb_boardinfo *boardinfo, + const char *prefix) + { +- nvram_read_u16(prefix, NULL, "boardvendor", &boardinfo->vendor, 0); ++ nvram_read_u16(prefix, NULL, "boardvendor", &boardinfo->vendor, 0, ++ true); + if (!boardinfo->vendor) + boardinfo->vendor = SSB_BOARDVENDOR_BCM; + +- nvram_read_u16(prefix, NULL, "boardtype", &boardinfo->type, 0); ++ nvram_read_u16(prefix, NULL, "boardtype", &boardinfo->type, 0, true); + } + #endif + +@@ -629,10 +746,11 @@ void bcm47xx_fill_ssb_boardinfo(struct s + void bcm47xx_fill_bcma_boardinfo(struct bcma_boardinfo *boardinfo, + const char *prefix) + { +- nvram_read_u16(prefix, NULL, "boardvendor", &boardinfo->vendor, 0); ++ nvram_read_u16(prefix, NULL, "boardvendor", &boardinfo->vendor, 0, ++ true); + if (!boardinfo->vendor) + boardinfo->vendor = SSB_BOARDVENDOR_BCM; + +- nvram_read_u16(prefix, NULL, "boardtype", &boardinfo->type, 0); ++ nvram_read_u16(prefix, NULL, "boardtype", &boardinfo->type, 0, true); + } + #endif +--- a/arch/mips/include/asm/mach-bcm47xx/bcm47xx.h ++++ b/arch/mips/include/asm/mach-bcm47xx/bcm47xx.h +@@ -44,8 +44,8 @@ union bcm47xx_bus { + extern union bcm47xx_bus bcm47xx_bus; + extern enum bcm47xx_bus_type bcm47xx_bus_type; + +-void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix); +-void bcm47xx_fill_sprom_ethernet(struct ssb_sprom *sprom, const char *prefix); ++void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix, ++ bool fallback); + + #ifdef CONFIG_BCM47XX_SSB + void bcm47xx_fill_ssb_boardinfo(struct ssb_boardinfo *boardinfo, diff --git a/target/linux/brcm47xx/patches-3.3/201-bcma-just-do-the-necessary-things-in-early-register.patch b/target/linux/brcm47xx/patches-3.3/201-bcma-just-do-the-necessary-things-in-early-register.patch new file mode 100644 index 000000000..6ef5ecd51 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/201-bcma-just-do-the-necessary-things-in-early-register.patch @@ -0,0 +1,181 @@ +--- a/drivers/bcma/driver_chipcommon.c ++++ b/drivers/bcma/driver_chipcommon.c +@@ -22,12 +22,9 @@ static inline u32 bcma_cc_write32_masked + return value; + } + +-void bcma_core_chipcommon_init(struct bcma_drv_cc *cc) ++void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc) + { +- u32 leddc_on = 10; +- u32 leddc_off = 90; +- +- if (cc->setup_done) ++ if (cc->early_setup_done) + return; + + if (cc->core->id.rev >= 11) +@@ -36,6 +33,22 @@ void bcma_core_chipcommon_init(struct bc + if (cc->core->id.rev >= 35) + cc->capabilities_ext = bcma_cc_read32(cc, BCMA_CC_CAP_EXT); + ++ if (cc->capabilities & BCMA_CC_CAP_PMU) ++ bcma_pmu_early_init(cc); ++ ++ cc->early_setup_done = true; ++} ++ ++void bcma_core_chipcommon_init(struct bcma_drv_cc *cc) ++{ ++ u32 leddc_on = 10; ++ u32 leddc_off = 90; ++ ++ if (cc->setup_done) ++ return; ++ ++ bcma_core_chipcommon_early_init(cc); ++ + if (cc->core->id.rev >= 20) { + bcma_cc_write32(cc, BCMA_CC_GPIOPULLUP, 0); + bcma_cc_write32(cc, BCMA_CC_GPIOPULLDOWN, 0); +--- a/drivers/bcma/driver_chipcommon_pmu.c ++++ b/drivers/bcma/driver_chipcommon_pmu.c +@@ -141,7 +141,7 @@ void bcma_pmu_workarounds(struct bcma_dr + } + } + +-void bcma_pmu_init(struct bcma_drv_cc *cc) ++void bcma_pmu_early_init(struct bcma_drv_cc *cc) + { + u32 pmucap; + +@@ -150,7 +150,10 @@ void bcma_pmu_init(struct bcma_drv_cc *c + + bcma_debug(cc->core->bus, "Found rev %u PMU (capabilities 0x%08X)\n", + cc->pmu.rev, pmucap); ++} + ++void bcma_pmu_init(struct bcma_drv_cc *cc) ++{ + if (cc->pmu.rev == 1) + bcma_cc_mask32(cc, BCMA_CC_PMU_CTL, + ~BCMA_CC_PMU_CTL_NOILPONW); +--- a/drivers/bcma/driver_mips.c ++++ b/drivers/bcma/driver_mips.c +@@ -222,16 +222,33 @@ static void bcma_core_mips_flash_detect( + } + } + ++void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) ++{ ++ struct bcma_bus *bus = mcore->core->bus; ++ ++ if (mcore->early_setup_done) ++ return; ++ ++ bcma_chipco_serial_init(&bus->drv_cc); ++ bcma_core_mips_flash_detect(mcore); ++ ++ mcore->early_setup_done = true; ++} ++ + void bcma_core_mips_init(struct bcma_drv_mips *mcore) + { + struct bcma_bus *bus; + struct bcma_device *core; + bus = mcore->core->bus; + ++ if (mcore->setup_done) ++ return; ++ + bcma_info(bus, "Initializing MIPS core...\n"); + +- if (!mcore->setup_done) +- mcore->assigned_irqs = 1; ++ bcma_core_mips_early_init(mcore); ++ ++ mcore->assigned_irqs = 1; + + /* Assign IRQs to all cores on the bus */ + list_for_each_entry(core, &bus->cores, list) { +@@ -266,10 +283,5 @@ void bcma_core_mips_init(struct bcma_drv + bcma_info(bus, "IRQ reconfiguration done\n"); + bcma_core_mips_dump_irq(bus); + +- if (mcore->setup_done) +- return; +- +- bcma_chipco_serial_init(&bus->drv_cc); +- bcma_core_mips_flash_detect(mcore); + mcore->setup_done = true; + } +--- a/drivers/bcma/main.c ++++ b/drivers/bcma/main.c +@@ -247,18 +247,18 @@ int __init bcma_bus_early_register(struc + return -1; + } + +- /* Init CC core */ ++ /* Early init CC core */ + core = bcma_find_core(bus, bcma_cc_core_id(bus)); + if (core) { + bus->drv_cc.core = core; +- bcma_core_chipcommon_init(&bus->drv_cc); ++ bcma_core_chipcommon_early_init(&bus->drv_cc); + } + +- /* Init MIPS core */ ++ /* Early init MIPS core */ + core = bcma_find_core(bus, BCMA_CORE_MIPS_74K); + if (core) { + bus->drv_mips.core = core; +- bcma_core_mips_init(&bus->drv_mips); ++ bcma_core_mips_early_init(&bus->drv_mips); + } + + bcma_info(bus, "Early bus registered\n"); +--- a/include/linux/bcma/bcma_driver_chipcommon.h ++++ b/include/linux/bcma/bcma_driver_chipcommon.h +@@ -476,6 +476,7 @@ struct bcma_drv_cc { + u32 capabilities; + u32 capabilities_ext; + u8 setup_done:1; ++ u8 early_setup_done:1; + /* Fast Powerup Delay constant */ + u16 fast_pwrup_delay; + struct bcma_chipcommon_pmu pmu; +@@ -510,6 +511,7 @@ struct bcma_drv_cc { + bcma_cc_write32(cc, offset, (bcma_cc_read32(cc, offset) & (mask)) | (set)) + + extern void bcma_core_chipcommon_init(struct bcma_drv_cc *cc); ++extern void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc); + + extern void bcma_chipco_suspend(struct bcma_drv_cc *cc); + extern void bcma_chipco_resume(struct bcma_drv_cc *cc); +@@ -533,6 +535,7 @@ u32 bcma_chipco_gpio_polarity(struct bcm + + /* PMU support */ + extern void bcma_pmu_init(struct bcma_drv_cc *cc); ++extern void bcma_pmu_early_init(struct bcma_drv_cc *cc); + + extern void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, + u32 value); +--- a/include/linux/bcma/bcma_driver_mips.h ++++ b/include/linux/bcma/bcma_driver_mips.h +@@ -35,13 +35,16 @@ struct bcma_device; + struct bcma_drv_mips { + struct bcma_device *core; + u8 setup_done:1; ++ u8 early_setup_done:1; + unsigned int assigned_irqs; + }; + + #ifdef CONFIG_BCMA_DRIVER_MIPS + extern void bcma_core_mips_init(struct bcma_drv_mips *mcore); ++extern void bcma_core_mips_early_init(struct bcma_drv_mips *mcore); + #else + static inline void bcma_core_mips_init(struct bcma_drv_mips *mcore) { } ++static inline void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) { } + #endif + + extern u32 bcma_cpu_clock(struct bcma_drv_mips *mcore); diff --git a/target/linux/brcm47xx/patches-3.3/202-bcma-init-sprom-struct-earlier.patch b/target/linux/brcm47xx/patches-3.3/202-bcma-init-sprom-struct-earlier.patch new file mode 100644 index 000000000..03540f04c --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/202-bcma-init-sprom-struct-earlier.patch @@ -0,0 +1,37 @@ +--- a/drivers/bcma/main.c ++++ b/drivers/bcma/main.c +@@ -165,6 +165,20 @@ int __devinit bcma_bus_register(struct b + return -1; + } + ++ /* Early init CC core */ ++ core = bcma_find_core(bus, bcma_cc_core_id(bus)); ++ if (core) { ++ bus->drv_cc.core = core; ++ bcma_core_chipcommon_early_init(&bus->drv_cc); ++ } ++ ++ /* Try to get SPROM */ ++ err = bcma_sprom_get(bus); ++ if (err == -ENOENT) { ++ bcma_err(bus, "No SPROM available\n"); ++ } else if (err) ++ bcma_err(bus, "Failed to get SPROM: %d\n", err); ++ + /* Init CC core */ + core = bcma_find_core(bus, bcma_cc_core_id(bus)); + if (core) { +@@ -193,13 +207,6 @@ int __devinit bcma_bus_register(struct b + bcma_core_gmac_cmn_init(&bus->drv_gmac_cmn); + } + +- /* Try to get SPROM */ +- err = bcma_sprom_get(bus); +- if (err == -ENOENT) { +- bcma_err(bus, "No SPROM available\n"); +- } else if (err) +- bcma_err(bus, "Failed to get SPROM: %d\n", err); +- + /* Register found cores */ + bcma_register_cores(bus); + diff --git a/target/linux/brcm47xx/patches-3.3/203-bcma-use-fallback-sprom-if-sprom-on-card-was-not-val.patch b/target/linux/brcm47xx/patches-3.3/203-bcma-use-fallback-sprom-if-sprom-on-card-was-not-val.patch new file mode 100644 index 000000000..b6e648056 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/203-bcma-use-fallback-sprom-if-sprom-on-card-was-not-val.patch @@ -0,0 +1,15 @@ +--- a/drivers/bcma/sprom.c ++++ b/drivers/bcma/sprom.c +@@ -591,8 +591,11 @@ int bcma_sprom_get(struct bcma_bus *bus) + bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true); + + err = bcma_sprom_valid(sprom); +- if (err) ++ if (err) { ++ bcma_warn(bus, "invalid sprom read from the PCIe card, try to use fallback sprom\n"); ++ err = bcma_fill_sprom_with_fallback(bus, &bus->sprom); + goto out; ++ } + + bcma_sprom_extract_r8(bus, sprom); + diff --git a/target/linux/brcm47xx/patches-3.3/204-bcma-do-not-initialize-deactivated-PCIe-cores.patch b/target/linux/brcm47xx/patches-3.3/204-bcma-do-not-initialize-deactivated-PCIe-cores.patch new file mode 100644 index 000000000..302ac8d95 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/204-bcma-do-not-initialize-deactivated-PCIe-cores.patch @@ -0,0 +1,26 @@ +--- a/drivers/bcma/driver_pci_host.c ++++ b/drivers/bcma/driver_pci_host.c +@@ -35,11 +35,6 @@ bool __devinit bcma_core_pci_is_in_hostm + chipid_top != 0x5300) + return false; + +- if (bus->sprom.boardflags_lo & BCMA_CORE_PCI_BFL_NOPCI) { +- bcma_info(bus, "This PCI core is disabled and not working\n"); +- return false; +- } +- + bcma_core_enable(pc->core, 0); + + return !mips_busprobe32(tmp, pc->core->io_addr); +@@ -396,6 +391,11 @@ void __devinit bcma_core_pci_hostmode_in + + bcma_info(bus, "PCIEcore in host mode found\n"); + ++ if (bus->sprom.boardflags_lo & BCMA_CORE_PCI_BFL_NOPCI) { ++ bcma_info(bus, "This PCIE core is disabled and not working\n"); ++ return; ++ } ++ + pc_host = kzalloc(sizeof(*pc_host), GFP_KERNEL); + if (!pc_host) { + bcma_err(bus, "can not allocate memory"); diff --git a/target/linux/brcm47xx/patches-3.3/210-b44_phy_fix.patch b/target/linux/brcm47xx/patches-3.3/210-b44_phy_fix.patch new file mode 100644 index 000000000..2d0536552 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/210-b44_phy_fix.patch @@ -0,0 +1,54 @@ +--- a/drivers/net/ethernet/broadcom/b44.c ++++ b/drivers/net/ethernet/broadcom/b44.c +@@ -410,10 +410,34 @@ static void b44_wap54g10_workaround(stru + error: + pr_warning("PHY: cannot reset MII transceiver isolate bit\n"); + } ++ ++static void b44_bcm47xx_workarounds(struct b44 *bp) ++{ ++ char buf[20]; ++ struct ssb_device *sdev = bp->sdev; ++ ++ /* Toshiba WRC-1000, Siemens SE505 v1, Askey RT-210W, RT-220W */ ++ if (sdev->bus->sprom.board_num == 100) { ++ bp->phy_addr = B44_PHY_ADDR_NO_PHY; ++ } else { ++ /* WL-HDD */ ++ if (nvram_getenv("hardware_version", buf, sizeof(buf)) >= 0 && ++ !strncmp(buf, "WL300-", strlen("WL300-"))) { ++ if (sdev->bus->sprom.et0phyaddr == 0 && ++ sdev->bus->sprom.et1phyaddr == 1) ++ bp->phy_addr = B44_PHY_ADDR_NO_PHY; ++ } ++ } ++ return; ++} + #else + static inline void b44_wap54g10_workaround(struct b44 *bp) + { + } ++ ++static inline void b44_bcm47xx_workarounds(struct b44 *bp) ++{ ++} + #endif + + static int b44_setup_phy(struct b44 *bp) +@@ -422,6 +446,7 @@ static int b44_setup_phy(struct b44 *bp) + int err; + + b44_wap54g10_workaround(bp); ++ b44_bcm47xx_workarounds(bp); + + if (bp->phy_addr == B44_PHY_ADDR_NO_PHY) + return 0; +@@ -2088,6 +2113,8 @@ static int __devinit b44_get_invariants( + * valid PHY address. */ + bp->phy_addr &= 0x1F; + ++ b44_bcm47xx_workarounds(bp); ++ + memcpy(bp->dev->dev_addr, addr, 6); + + if (!is_valid_ether_addr(&bp->dev->dev_addr[0])){ diff --git a/target/linux/brcm47xx/patches-3.3/211-b44_timeout_spam.patch b/target/linux/brcm47xx/patches-3.3/211-b44_timeout_spam.patch new file mode 100644 index 000000000..c2eb3ad90 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/211-b44_timeout_spam.patch @@ -0,0 +1,15 @@ +--- a/drivers/net/ethernet/broadcom/b44.c ++++ b/drivers/net/ethernet/broadcom/b44.c +@@ -187,10 +187,11 @@ static int b44_wait_bit(struct b44 *bp, + udelay(10); + } + if (i == timeout) { ++#if 0 + if (net_ratelimit()) + netdev_err(bp->dev, "BUG! Timeout waiting for bit %08x of register %lx to %s\n", + bit, reg, clear ? "clear" : "set"); +- ++#endif + return -ENODEV; + } + return 0; diff --git a/target/linux/brcm47xx/patches-3.3/240-bcma-pcie-config-access.patch b/target/linux/brcm47xx/patches-3.3/240-bcma-pcie-config-access.patch new file mode 100644 index 000000000..91b8a4191 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/240-bcma-pcie-config-access.patch @@ -0,0 +1,104 @@ +--- a/drivers/bcma/driver_pci_host.c ++++ b/drivers/bcma/driver_pci_host.c +@@ -94,19 +94,19 @@ static int bcma_extpci_read_config(struc + if (dev == 0) { + /* we support only two functions on device 0 */ + if (func > 1) +- return -EINVAL; ++ goto out; + + /* accesses to config registers with offsets >= 256 + * requires indirect access. + */ + if (off >= PCI_CONFIG_SPACE_SIZE) { + addr = (func << 12); +- addr |= (off & 0x0FFF); ++ addr |= (off & 0x0FFC); + val = bcma_pcie_read_config(pc, addr); + } else { + addr = BCMA_CORE_PCI_PCICFG0; + addr |= (func << 8); +- addr |= (off & 0xfc); ++ addr |= (off & 0xFC); + val = pcicore_read32(pc, addr); + } + } else { +@@ -122,8 +122,6 @@ static int bcma_extpci_read_config(struc + val = 0xffffffff; + goto unmap; + } +- +- val = readl(mmio); + } + val >>= (8 * (off & 3)); + +@@ -151,7 +149,7 @@ static int bcma_extpci_write_config(stru + const void *buf, int len) + { + int err = -EINVAL; +- u32 addr = 0, val = 0; ++ u32 addr, val; + void __iomem *mmio = 0; + u16 chipid = pc->core->bus->chipinfo.id; + +@@ -159,16 +157,22 @@ static int bcma_extpci_write_config(stru + if (unlikely(len != 1 && len != 2 && len != 4)) + goto out; + if (dev == 0) { ++ /* we support only two functions on device 0 */ ++ if (func > 1) ++ goto out; ++ + /* accesses to config registers with offsets >= 256 + * requires indirect access. + */ +- if (off < PCI_CONFIG_SPACE_SIZE) { +- addr = pc->core->addr + BCMA_CORE_PCI_PCICFG0; ++ if (off >= PCI_CONFIG_SPACE_SIZE) { ++ addr = (func << 12); ++ addr |= (off & 0x0FFC); ++ val = bcma_pcie_read_config(pc, addr); ++ } else { ++ addr = BCMA_CORE_PCI_PCICFG0; + addr |= (func << 8); +- addr |= (off & 0xfc); +- mmio = ioremap_nocache(addr, sizeof(val)); +- if (!mmio) +- goto out; ++ addr |= (off & 0xFC); ++ val = pcicore_read32(pc, addr); + } + } else { + addr = bcma_get_cfgspace_addr(pc, dev, func, off); +@@ -187,12 +191,10 @@ static int bcma_extpci_write_config(stru + + switch (len) { + case 1: +- val = readl(mmio); + val &= ~(0xFF << (8 * (off & 3))); + val |= *((const u8 *)buf) << (8 * (off & 3)); + break; + case 2: +- val = readl(mmio); + val &= ~(0xFFFF << (8 * (off & 3))); + val |= *((const u16 *)buf) << (8 * (off & 3)); + break; +@@ -200,13 +202,14 @@ static int bcma_extpci_write_config(stru + val = *((const u32 *)buf); + break; + } +- if (dev == 0 && !addr) { ++ if (dev == 0) { + /* accesses to config registers with offsets >= 256 + * requires indirect access. + */ +- addr = (func << 12); +- addr |= (off & 0x0FFF); +- bcma_pcie_write_config(pc, addr, val); ++ if (off >= PCI_CONFIG_SPACE_SIZE) ++ bcma_pcie_write_config(pc, addr, val); ++ else ++ pcicore_write32(pc, addr, val); + } else { + writel(val, mmio); + diff --git a/target/linux/brcm47xx/patches-3.3/280-activate_ssb_support_in_usb.patch b/target/linux/brcm47xx/patches-3.3/280-activate_ssb_support_in_usb.patch new file mode 100644 index 000000000..c535c05db --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/280-activate_ssb_support_in_usb.patch @@ -0,0 +1,25 @@ +This prevents the options from being delete with make kernel_oldconfig. +--- + drivers/ssb/Kconfig | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/bcma/Kconfig ++++ b/drivers/bcma/Kconfig +@@ -37,6 +37,7 @@ config BCMA_DRIVER_PCI_HOSTMODE + config BCMA_HOST_SOC + bool + depends on BCMA_DRIVER_MIPS ++ select USB_HCD_BCMA if USB_EHCI_HCD || USB_OHCI_HCD + + config BCMA_SFLASH + bool +--- a/drivers/ssb/Kconfig ++++ b/drivers/ssb/Kconfig +@@ -147,6 +147,7 @@ config SSB_SFLASH + config SSB_EMBEDDED + bool + depends on SSB_DRIVER_MIPS ++ select USB_HCD_SSB if USB_EHCI_HCD || USB_OHCI_HCD + default y + + config SSB_DRIVER_EXTIF diff --git a/target/linux/brcm47xx/patches-3.3/300-fork_cacheflush.patch b/target/linux/brcm47xx/patches-3.3/300-fork_cacheflush.patch new file mode 100644 index 000000000..686fb1b94 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/300-fork_cacheflush.patch @@ -0,0 +1,11 @@ +--- a/arch/mips/include/asm/cacheflush.h ++++ b/arch/mips/include/asm/cacheflush.h +@@ -32,7 +32,7 @@ + extern void (*flush_cache_all)(void); + extern void (*__flush_cache_all)(void); + extern void (*flush_cache_mm)(struct mm_struct *mm); +-#define flush_cache_dup_mm(mm) do { (void) (mm); } while (0) ++#define flush_cache_dup_mm(mm) flush_cache_mm(mm) + extern void (*flush_cache_range)(struct vm_area_struct *vma, + unsigned long start, unsigned long end); + extern void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page, unsigned long pfn); diff --git a/target/linux/brcm47xx/patches-3.3/310-no_highpage.patch b/target/linux/brcm47xx/patches-3.3/310-no_highpage.patch new file mode 100644 index 000000000..aa55a086f --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/310-no_highpage.patch @@ -0,0 +1,66 @@ +--- a/arch/mips/include/asm/page.h ++++ b/arch/mips/include/asm/page.h +@@ -48,6 +48,7 @@ + #ifndef __ASSEMBLY__ + + #include <linux/pfn.h> ++#include <asm/cpu-features.h> + #include <asm/io.h> + + extern void build_clear_page(void); +@@ -83,13 +84,16 @@ static inline void clear_user_page(void + flush_data_cache_page((unsigned long)addr); + } + +-extern void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, +- struct page *to); +-struct vm_area_struct; +-extern void copy_user_highpage(struct page *to, struct page *from, +- unsigned long vaddr, struct vm_area_struct *vma); ++static inline void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, ++ struct page *to) ++{ ++ extern void (*flush_data_cache_page)(unsigned long addr); + +-#define __HAVE_ARCH_COPY_USER_HIGHPAGE ++ copy_page(vto, vfrom); ++ if (!cpu_has_ic_fills_f_dc || ++ pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK)) ++ flush_data_cache_page((unsigned long)vto); ++} + + /* + * These are used to make use of C type-checking.. +--- a/arch/mips/mm/init.c ++++ b/arch/mips/mm/init.c +@@ -202,30 +202,6 @@ void kunmap_coherent(void) + preempt_check_resched(); + } + +-void copy_user_highpage(struct page *to, struct page *from, +- unsigned long vaddr, struct vm_area_struct *vma) +-{ +- void *vfrom, *vto; +- +- vto = kmap_atomic(to, KM_USER1); +- if (cpu_has_dc_aliases && cpu_use_kmap_coherent && +- page_mapped(from) && !Page_dcache_dirty(from)) { +- vfrom = kmap_coherent(from, vaddr); +- copy_page(vto, vfrom); +- kunmap_coherent(); +- } else { +- vfrom = kmap_atomic(from, KM_USER0); +- copy_page(vto, vfrom); +- kunmap_atomic(vfrom, KM_USER0); +- } +- if ((!cpu_has_ic_fills_f_dc) || +- pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK)) +- flush_data_cache_page((unsigned long)vto); +- kunmap_atomic(vto, KM_USER1); +- /* Make sure this page is cleared on other CPU's too before using it */ +- smp_wmb(); +-} +- + void copy_to_user_page(struct vm_area_struct *vma, + struct page *page, unsigned long vaddr, void *dst, const void *src, + unsigned long len) diff --git a/target/linux/brcm47xx/patches-3.3/400-arch-bcm47xx.patch b/target/linux/brcm47xx/patches-3.3/400-arch-bcm47xx.patch new file mode 100644 index 000000000..784562dc7 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/400-arch-bcm47xx.patch @@ -0,0 +1,56 @@ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -274,3 +274,30 @@ int nvram_getenv(char *name, char *val, + return NVRAM_ERR_ENVNOTFOUND; + } + EXPORT_SYMBOL(nvram_getenv); ++ ++char *nvram_get(const char *name) ++{ ++ char *var, *value, *end, *eq; ++ ++ if (!name) ++ return NULL; ++ ++ if (!nvram_buf[0]) ++ early_nvram_init(); ++ ++ /* Look for name=value and return value */ ++ var = &nvram_buf[sizeof(struct nvram_header)]; ++ end = nvram_buf + sizeof(nvram_buf) - 2; ++ end[0] = end[1] = '\0'; ++ for (; *var; var = value + strlen(value) + 1) { ++ eq = strchr(var, '='); ++ if (!eq) ++ break; ++ value = eq + 1; ++ if ((eq - var) == strlen(name) && strncmp(var, name, (eq - var)) == 0) ++ return value; ++ } ++ ++ return NULL; ++} ++EXPORT_SYMBOL(nvram_get); +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -381,3 +381,20 @@ static int __init bcm47xx_register_flash + return -1; + } + fs_initcall(bcm47xx_register_flash); ++ ++static int __init bcm47xx_register_gpiodev(void) ++{ ++ static struct resource res = { ++ .start = 0xFFFFFFFF, ++ }; ++ struct platform_device *pdev; ++ ++ pdev = platform_device_register_simple("GPIODEV", 0, &res, 1); ++ if (!pdev) { ++ printk(KERN_ERR "bcm47xx: GPIODEV init failed\n"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++device_initcall(bcm47xx_register_gpiodev); diff --git a/target/linux/brcm47xx/patches-3.3/500-ssb-add-function-to-return-number-of-gpio-lines.patch b/target/linux/brcm47xx/patches-3.3/500-ssb-add-function-to-return-number-of-gpio-lines.patch new file mode 100644 index 000000000..f1b483e4a --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/500-ssb-add-function-to-return-number-of-gpio-lines.patch @@ -0,0 +1,40 @@ +--- a/drivers/ssb/embedded.c ++++ b/drivers/ssb/embedded.c +@@ -136,6 +136,18 @@ u32 ssb_gpio_polarity(struct ssb_bus *bu + } + EXPORT_SYMBOL(ssb_gpio_polarity); + ++int ssb_gpio_count(struct ssb_bus *bus) ++{ ++ if (ssb_chipco_available(&bus->chipco)) ++ return SSB_GPIO_CHIPCO_LINES; ++ else if (ssb_extif_available(&bus->extif)) ++ return SSB_GPIO_EXTIF_LINES; ++ else ++ SSB_WARN_ON(1); ++ return 0; ++} ++EXPORT_SYMBOL(ssb_gpio_count); ++ + #ifdef CONFIG_SSB_DRIVER_GIGE + static int gige_pci_init_callback(struct ssb_bus *bus, unsigned long data) + { +--- a/include/linux/ssb/ssb_embedded.h ++++ b/include/linux/ssb/ssb_embedded.h +@@ -7,6 +7,9 @@ + + extern int ssb_watchdog_timer_set(struct ssb_bus *bus, u32 ticks); + ++#define SSB_GPIO_EXTIF_LINES 5 ++#define SSB_GPIO_CHIPCO_LINES 16 ++ + /* Generic GPIO API */ + u32 ssb_gpio_in(struct ssb_bus *bus, u32 mask); + u32 ssb_gpio_out(struct ssb_bus *bus, u32 mask, u32 value); +@@ -14,5 +17,6 @@ u32 ssb_gpio_outen(struct ssb_bus *bus, + u32 ssb_gpio_control(struct ssb_bus *bus, u32 mask, u32 value); + u32 ssb_gpio_intmask(struct ssb_bus *bus, u32 mask, u32 value); + u32 ssb_gpio_polarity(struct ssb_bus *bus, u32 mask, u32 value); ++int ssb_gpio_count(struct ssb_bus *bus); + + #endif /* LINUX_SSB_EMBEDDED_H_ */ diff --git a/target/linux/brcm47xx/patches-3.3/501-bcma-add-gpio-driver.patch b/target/linux/brcm47xx/patches-3.3/501-bcma-add-gpio-driver.patch new file mode 100644 index 000000000..951c6d40e --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/501-bcma-add-gpio-driver.patch @@ -0,0 +1,140 @@ +--- a/drivers/bcma/driver_chipcommon.c ++++ b/drivers/bcma/driver_chipcommon.c +@@ -70,6 +70,8 @@ void bcma_core_chipcommon_init(struct bc + (leddc_off << BCMA_CC_GPIOTIMER_OFFTIME_SHIFT))); + } + ++ spin_lock_init(&cc->gpio_lock); ++ + cc->setup_done = true; + } + +@@ -92,34 +94,81 @@ u32 bcma_chipco_irq_status(struct bcma_d + + u32 bcma_chipco_gpio_in(struct bcma_drv_cc *cc, u32 mask) + { +- return bcma_cc_read32(cc, BCMA_CC_GPIOIN) & mask; ++ unsigned long flags; ++ u32 res; ++ ++ spin_lock_irqsave(&cc->gpio_lock, flags); ++ res = bcma_cc_read32(cc, BCMA_CC_GPIOIN) & mask; ++ spin_unlock_irqrestore(&cc->gpio_lock, flags); ++ ++ return res; + } ++EXPORT_SYMBOL_GPL(bcma_chipco_gpio_in); + + u32 bcma_chipco_gpio_out(struct bcma_drv_cc *cc, u32 mask, u32 value) + { +- return bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUT, mask, value); ++ unsigned long flags; ++ u32 res; ++ ++ spin_lock_irqsave(&cc->gpio_lock, flags); ++ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUT, mask, value); ++ spin_unlock_irqrestore(&cc->gpio_lock, flags); ++ ++ return res; + } ++EXPORT_SYMBOL_GPL(bcma_chipco_gpio_out); + + u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value) + { +- return bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUTEN, mask, value); ++ unsigned long flags; ++ u32 res; ++ ++ spin_lock_irqsave(&cc->gpio_lock, flags); ++ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUTEN, mask, value); ++ spin_unlock_irqrestore(&cc->gpio_lock, flags); ++ ++ return res; + } ++EXPORT_SYMBOL_GPL(bcma_chipco_gpio_outen); + + u32 bcma_chipco_gpio_control(struct bcma_drv_cc *cc, u32 mask, u32 value) + { +- return bcma_cc_write32_masked(cc, BCMA_CC_GPIOCTL, mask, value); ++ unsigned long flags; ++ u32 res; ++ ++ spin_lock_irqsave(&cc->gpio_lock, flags); ++ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOCTL, mask, value); ++ spin_unlock_irqrestore(&cc->gpio_lock, flags); ++ ++ return res; + } + EXPORT_SYMBOL_GPL(bcma_chipco_gpio_control); + + u32 bcma_chipco_gpio_intmask(struct bcma_drv_cc *cc, u32 mask, u32 value) + { +- return bcma_cc_write32_masked(cc, BCMA_CC_GPIOIRQ, mask, value); ++ unsigned long flags; ++ u32 res; ++ ++ spin_lock_irqsave(&cc->gpio_lock, flags); ++ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOIRQ, mask, value); ++ spin_unlock_irqrestore(&cc->gpio_lock, flags); ++ ++ return res; + } ++EXPORT_SYMBOL_GPL(bcma_chipco_gpio_intmask); + + u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value) + { +- return bcma_cc_write32_masked(cc, BCMA_CC_GPIOPOL, mask, value); ++ unsigned long flags; ++ u32 res; ++ ++ spin_lock_irqsave(&cc->gpio_lock, flags); ++ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOPOL, mask, value); ++ spin_unlock_irqrestore(&cc->gpio_lock, flags); ++ ++ return res; + } ++EXPORT_SYMBOL_GPL(bcma_chipco_gpio_polarity); + + #ifdef CONFIG_BCMA_DRIVER_MIPS + void bcma_chipco_serial_init(struct bcma_drv_cc *cc) +--- a/include/linux/bcma/bcma_driver_chipcommon.h ++++ b/include/linux/bcma/bcma_driver_chipcommon.h +@@ -495,6 +495,9 @@ struct bcma_drv_cc { + int nr_serial_ports; + struct bcma_serial_port serial_ports[4]; + #endif /* CONFIG_BCMA_DRIVER_MIPS */ ++ ++ /* Lock for GPIO register access. */ ++ spinlock_t gpio_lock; + }; + + /* Register access */ +@@ -525,13 +528,22 @@ void bcma_chipco_irq_mask(struct bcma_dr + + u32 bcma_chipco_irq_status(struct bcma_drv_cc *cc, u32 mask); + ++#define BCMA_CC_GPIO_LINES 16 ++ + /* Chipcommon GPIO pin access. */ +-u32 bcma_chipco_gpio_in(struct bcma_drv_cc *cc, u32 mask); +-u32 bcma_chipco_gpio_out(struct bcma_drv_cc *cc, u32 mask, u32 value); +-u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value); +-u32 bcma_chipco_gpio_control(struct bcma_drv_cc *cc, u32 mask, u32 value); +-u32 bcma_chipco_gpio_intmask(struct bcma_drv_cc *cc, u32 mask, u32 value); +-u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value); ++extern u32 bcma_chipco_gpio_in(struct bcma_drv_cc *cc, u32 mask); ++extern u32 bcma_chipco_gpio_out(struct bcma_drv_cc *cc, u32 mask, u32 value); ++extern u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value); ++extern u32 bcma_chipco_gpio_control(struct bcma_drv_cc *cc, u32 mask, ++ u32 value); ++extern u32 bcma_chipco_gpio_intmask(struct bcma_drv_cc *cc, u32 mask, ++ u32 value); ++extern u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, ++ u32 value); ++static inline int bcma_chipco_gpio_count(void) ++{ ++ return BCMA_CC_GPIO_LINES; ++} + + /* PMU support */ + extern void bcma_pmu_init(struct bcma_drv_cc *cc); diff --git a/target/linux/brcm47xx/patches-3.3/502-bcm47xx-rewrite-gpio-handling.patch b/target/linux/brcm47xx/patches-3.3/502-bcm47xx-rewrite-gpio-handling.patch new file mode 100644 index 000000000..2f62728d6 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/502-bcm47xx-rewrite-gpio-handling.patch @@ -0,0 +1,493 @@ +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -92,6 +92,7 @@ config ATH79 + + config BCM47XX + bool "Broadcom BCM47XX based boards" ++ select ARCH_REQUIRE_GPIOLIB + select BOOT_RAW + select CEVT_R4K + select CSRC_R4K +@@ -101,7 +102,6 @@ config BCM47XX + select NO_EXCEPT_FILL + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN +- select GENERIC_GPIO + select CFE + help + Support for BCM47XX based boards +--- a/arch/mips/bcm47xx/gpio.c ++++ b/arch/mips/bcm47xx/gpio.c +@@ -4,83 +4,154 @@ + * for more details. + * + * Copyright (C) 2007 Aurelien Jarno <aurelien@aurel32.net> ++ * Copyright (C) 2012 Hauke Mehrtens <hauke@hauke-m.de> ++ * ++ * Parts of this file are based on Atheros AR71XX/AR724X/AR913X GPIO + */ + + #include <linux/export.h> ++#include <linux/gpio.h> + #include <linux/ssb/ssb.h> +-#include <linux/ssb/ssb_driver_chipcommon.h> +-#include <linux/ssb/ssb_driver_extif.h> +-#include <asm/mach-bcm47xx/bcm47xx.h> +-#include <asm/mach-bcm47xx/gpio.h> ++#include <linux/ssb/ssb_embedded.h> ++#include <linux/bcma/bcma.h> ++ ++#include <bcm47xx.h> + +-#if (BCM47XX_CHIPCO_GPIO_LINES > BCM47XX_EXTIF_GPIO_LINES) +-static DECLARE_BITMAP(gpio_in_use, BCM47XX_CHIPCO_GPIO_LINES); +-#else +-static DECLARE_BITMAP(gpio_in_use, BCM47XX_EXTIF_GPIO_LINES); +-#endif + +-int gpio_request(unsigned gpio, const char *tag) ++static unsigned long bcm47xx_gpio_count; ++ ++/* low level BCM47xx gpio api */ ++u32 bcm47xx_gpio_in(u32 mask) + { + switch (bcm47xx_bus_type) { + #ifdef CONFIG_BCM47XX_SSB + case BCM47XX_BUS_TYPE_SSB: +- if (ssb_chipco_available(&bcm47xx_bus.ssb.chipco) && +- ((unsigned)gpio >= BCM47XX_CHIPCO_GPIO_LINES)) +- return -EINVAL; +- +- if (ssb_extif_available(&bcm47xx_bus.ssb.extif) && +- ((unsigned)gpio >= BCM47XX_EXTIF_GPIO_LINES)) +- return -EINVAL; +- +- if (test_and_set_bit(gpio, gpio_in_use)) +- return -EBUSY; +- +- return 0; ++ return ssb_gpio_in(&bcm47xx_bus.ssb, mask); + #endif + #ifdef CONFIG_BCM47XX_BCMA + case BCM47XX_BUS_TYPE_BCMA: +- if (gpio >= BCM47XX_CHIPCO_GPIO_LINES) +- return -EINVAL; +- +- if (test_and_set_bit(gpio, gpio_in_use)) +- return -EBUSY; ++ return bcma_chipco_gpio_in(&bcm47xx_bus.bcma.bus.drv_cc, mask); ++#endif ++ } ++ return -EINVAL; ++} ++EXPORT_SYMBOL(bcm47xx_gpio_in); + +- return 0; ++u32 bcm47xx_gpio_out(u32 mask, u32 value) ++{ ++ switch (bcm47xx_bus_type) { ++#ifdef CONFIG_BCM47XX_SSB ++ case BCM47XX_BUS_TYPE_SSB: ++ return ssb_gpio_out(&bcm47xx_bus.ssb, mask, value); ++#endif ++#ifdef CONFIG_BCM47XX_BCMA ++ case BCM47XX_BUS_TYPE_BCMA: ++ return bcma_chipco_gpio_out(&bcm47xx_bus.bcma.bus.drv_cc, mask, ++ value); + #endif + } + return -EINVAL; + } +-EXPORT_SYMBOL(gpio_request); ++EXPORT_SYMBOL(bcm47xx_gpio_out); + +-void gpio_free(unsigned gpio) ++u32 bcm47xx_gpio_outen(u32 mask, u32 value) + { + switch (bcm47xx_bus_type) { + #ifdef CONFIG_BCM47XX_SSB + case BCM47XX_BUS_TYPE_SSB: +- if (ssb_chipco_available(&bcm47xx_bus.ssb.chipco) && +- ((unsigned)gpio >= BCM47XX_CHIPCO_GPIO_LINES)) +- return; ++ return ssb_gpio_outen(&bcm47xx_bus.ssb, mask, value); ++#endif ++#ifdef CONFIG_BCM47XX_BCMA ++ case BCM47XX_BUS_TYPE_BCMA: ++ return bcma_chipco_gpio_outen(&bcm47xx_bus.bcma.bus.drv_cc, ++ mask, value); ++#endif ++ } ++ return -EINVAL; ++} ++EXPORT_SYMBOL(bcm47xx_gpio_outen); + +- if (ssb_extif_available(&bcm47xx_bus.ssb.extif) && +- ((unsigned)gpio >= BCM47XX_EXTIF_GPIO_LINES)) +- return; ++u32 bcm47xx_gpio_control(u32 mask, u32 value) ++{ ++ switch (bcm47xx_bus_type) { ++#ifdef CONFIG_BCM47XX_SSB ++ case BCM47XX_BUS_TYPE_SSB: ++ return ssb_gpio_control(&bcm47xx_bus.ssb, mask, value); ++#endif ++#ifdef CONFIG_BCM47XX_BCMA ++ case BCM47XX_BUS_TYPE_BCMA: ++ return bcma_chipco_gpio_control(&bcm47xx_bus.bcma.bus.drv_cc, ++ mask, value); ++#endif ++ } ++ return -EINVAL; ++} ++EXPORT_SYMBOL(bcm47xx_gpio_control); + +- clear_bit(gpio, gpio_in_use); +- return; ++u32 bcm47xx_gpio_intmask(u32 mask, u32 value) ++{ ++ switch (bcm47xx_bus_type) { ++#ifdef CONFIG_BCM47XX_SSB ++ case BCM47XX_BUS_TYPE_SSB: ++ return ssb_gpio_intmask(&bcm47xx_bus.ssb, mask, value); + #endif + #ifdef CONFIG_BCM47XX_BCMA + case BCM47XX_BUS_TYPE_BCMA: +- if (gpio >= BCM47XX_CHIPCO_GPIO_LINES) +- return; ++ return bcma_chipco_gpio_intmask(&bcm47xx_bus.bcma.bus.drv_cc, ++ mask, value); ++#endif ++ } ++ return -EINVAL; ++} ++EXPORT_SYMBOL(bcm47xx_gpio_intmask); + +- clear_bit(gpio, gpio_in_use); +- return; ++u32 bcm47xx_gpio_polarity(u32 mask, u32 value) ++{ ++ switch (bcm47xx_bus_type) { ++#ifdef CONFIG_BCM47XX_SSB ++ case BCM47XX_BUS_TYPE_SSB: ++ return ssb_gpio_polarity(&bcm47xx_bus.ssb, mask, value); ++#endif ++#ifdef CONFIG_BCM47XX_BCMA ++ case BCM47XX_BUS_TYPE_BCMA: ++ return bcma_chipco_gpio_polarity(&bcm47xx_bus.bcma.bus.drv_cc, ++ mask, value); + #endif + } ++ return -EINVAL; ++} ++EXPORT_SYMBOL(bcm47xx_gpio_polarity); ++ ++ ++static int bcm47xx_gpio_get_value(struct gpio_chip *chip, unsigned gpio) ++{ ++ return bcm47xx_gpio_in(1 << gpio); ++} ++ ++static void bcm47xx_gpio_set_value(struct gpio_chip *chip, ++ unsigned gpio, int value) ++{ ++ bcm47xx_gpio_out(1 << gpio, value ? 1 << gpio : 0); ++} ++ ++static int bcm47xx_gpio_direction_input(struct gpio_chip *chip, ++ unsigned gpio) ++{ ++ bcm47xx_gpio_outen(1 << gpio, 0); ++ return 0; ++} ++ ++static int bcm47xx_gpio_direction_output(struct gpio_chip *chip, ++ unsigned gpio, int value) ++{ ++ /* first set the gpio out value */ ++ bcm47xx_gpio_out(1 << gpio, value ? 1 << gpio : 0); ++ /* then set the gpio mode */ ++ bcm47xx_gpio_outen(1 << gpio, 1 << gpio); ++ return 0; + } +-EXPORT_SYMBOL(gpio_free); + +-int gpio_to_irq(unsigned gpio) ++static int bcm47xx_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) + { + switch (bcm47xx_bus_type) { + #ifdef CONFIG_BCM47XX_SSB +@@ -99,4 +170,55 @@ int gpio_to_irq(unsigned gpio) + } + return -EINVAL; + } +-EXPORT_SYMBOL_GPL(gpio_to_irq); ++ ++static struct gpio_chip bcm47xx_gpio_chip = { ++ .label = "bcm47xx", ++ .get = bcm47xx_gpio_get_value, ++ .set = bcm47xx_gpio_set_value, ++ .direction_input = bcm47xx_gpio_direction_input, ++ .direction_output = bcm47xx_gpio_direction_output, ++ .to_irq = bcm47xx_gpio_to_irq, ++ .base = 0, ++}; ++ ++void __init bcm47xx_gpio_init(void) ++{ ++ int err; ++ ++ switch (bcm47xx_bus_type) { ++#ifdef CONFIG_BCM47XX_SSB ++ case BCM47XX_BUS_TYPE_SSB: ++ bcm47xx_gpio_count = ssb_gpio_count(&bcm47xx_bus.ssb); ++ break; ++#endif ++#ifdef CONFIG_BCM47XX_BCMA ++ case BCM47XX_BUS_TYPE_BCMA: ++ bcm47xx_gpio_count = bcma_chipco_gpio_count(); ++ break; ++#endif ++ } ++ ++ bcm47xx_gpio_chip.ngpio = bcm47xx_gpio_count; ++ ++ err = gpiochip_add(&bcm47xx_gpio_chip); ++ if (err) ++ panic("cannot add BCM47xx GPIO chip, error=%d", err); ++} ++ ++int gpio_get_value(unsigned gpio) ++{ ++ if (gpio < bcm47xx_gpio_count) ++ return bcm47xx_gpio_in(1 << gpio); ++ ++ return __gpio_get_value(gpio); ++} ++EXPORT_SYMBOL(gpio_get_value); ++ ++void gpio_set_value(unsigned gpio, int value) ++{ ++ if (gpio < bcm47xx_gpio_count) ++ bcm47xx_gpio_out(1 << gpio, value ? 1 << gpio : 0); ++ else ++ __gpio_set_value(gpio, value); ++} ++EXPORT_SYMBOL(gpio_set_value); +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -346,6 +346,8 @@ void __init plat_mem_setup(void) + _machine_restart = bcm47xx_machine_restart; + _machine_halt = bcm47xx_machine_halt; + pm_power_off = bcm47xx_machine_halt; ++ ++ bcm47xx_gpio_init(); + } + + static int __init bcm47xx_register_bus_complete(void) +--- a/arch/mips/bcm47xx/wgt634u.c ++++ b/arch/mips/bcm47xx/wgt634u.c +@@ -133,6 +133,7 @@ static int __init wgt634u_init(void) + * been allocated ranges 00:09:5b:xx:xx:xx and 00:0f:b5:xx:xx:xx. + */ + u8 *et0mac; ++ int err; + + if (bcm47xx_bus_type != BCM47XX_BUS_TYPE_SSB) + return -ENODEV; +@@ -146,6 +147,12 @@ static int __init wgt634u_init(void) + + printk(KERN_INFO "WGT634U machine detected.\n"); + ++ err = gpio_request(WGT634U_GPIO_RESET, "reset-buton"); ++ if (err) { ++ printk(KERN_INFO "Can not register gpio for reset button\n"); ++ return 0; ++ } ++ + if (!request_irq(gpio_to_irq(WGT634U_GPIO_RESET), + gpio_interrupt, IRQF_SHARED, + "WGT634U GPIO", ccore)) { +--- a/arch/mips/include/asm/mach-bcm47xx/bcm47xx.h ++++ b/arch/mips/include/asm/mach-bcm47xx/bcm47xx.h +@@ -56,4 +56,6 @@ void bcm47xx_fill_bcma_boardinfo(struct + const char *prefix); + #endif + ++void bcm47xx_gpio_init(void); ++ + #endif /* __ASM_BCM47XX_H */ +--- a/arch/mips/include/asm/mach-bcm47xx/gpio.h ++++ b/arch/mips/include/asm/mach-bcm47xx/gpio.h +@@ -4,152 +4,42 @@ + * for more details. + * + * Copyright (C) 2007 Aurelien Jarno <aurelien@aurel32.net> ++ * Copyright (C) 2012 Hauke Mehrtens <hauke@hauke-m.de> + */ + + #ifndef __BCM47XX_GPIO_H + #define __BCM47XX_GPIO_H + +-#include <linux/ssb/ssb_embedded.h> +-#include <linux/bcma/bcma.h> +-#include <asm/mach-bcm47xx/bcm47xx.h> +- +-#define BCM47XX_EXTIF_GPIO_LINES 5 +-#define BCM47XX_CHIPCO_GPIO_LINES 16 +- +-extern int gpio_request(unsigned gpio, const char *label); +-extern void gpio_free(unsigned gpio); +-extern int gpio_to_irq(unsigned gpio); +- +-static inline int gpio_get_value(unsigned gpio) +-{ +- switch (bcm47xx_bus_type) { +-#ifdef CONFIG_BCM47XX_SSB +- case BCM47XX_BUS_TYPE_SSB: +- return ssb_gpio_in(&bcm47xx_bus.ssb, 1 << gpio); +-#endif +-#ifdef CONFIG_BCM47XX_BCMA +- case BCM47XX_BUS_TYPE_BCMA: +- return bcma_chipco_gpio_in(&bcm47xx_bus.bcma.bus.drv_cc, +- 1 << gpio); +-#endif +- } +- return -EINVAL; +-} +- +-#define gpio_get_value_cansleep gpio_get_value +- +-static inline void gpio_set_value(unsigned gpio, int value) +-{ +- switch (bcm47xx_bus_type) { +-#ifdef CONFIG_BCM47XX_SSB +- case BCM47XX_BUS_TYPE_SSB: +- ssb_gpio_out(&bcm47xx_bus.ssb, 1 << gpio, +- value ? 1 << gpio : 0); +- return; +-#endif +-#ifdef CONFIG_BCM47XX_BCMA +- case BCM47XX_BUS_TYPE_BCMA: +- bcma_chipco_gpio_out(&bcm47xx_bus.bcma.bus.drv_cc, 1 << gpio, +- value ? 1 << gpio : 0); +- return; +-#endif +- } +-} +- +-#define gpio_set_value_cansleep gpio_set_value +- +-static inline int gpio_cansleep(unsigned gpio) +-{ +- return 0; +-} +- +-static inline int gpio_is_valid(unsigned gpio) +-{ +- return gpio < (BCM47XX_EXTIF_GPIO_LINES + BCM47XX_CHIPCO_GPIO_LINES); +-} ++#define ARCH_NR_GPIOS 64 ++#include <asm-generic/gpio.h> + ++/* low level BCM47xx gpio api */ ++u32 bcm47xx_gpio_in(u32 mask); ++u32 bcm47xx_gpio_out(u32 mask, u32 value); ++u32 bcm47xx_gpio_outen(u32 mask, u32 value); ++u32 bcm47xx_gpio_control(u32 mask, u32 value); ++u32 bcm47xx_gpio_intmask(u32 mask, u32 value); ++u32 bcm47xx_gpio_polarity(u32 mask, u32 value); + +-static inline int gpio_direction_input(unsigned gpio) +-{ +- switch (bcm47xx_bus_type) { +-#ifdef CONFIG_BCM47XX_SSB +- case BCM47XX_BUS_TYPE_SSB: +- ssb_gpio_outen(&bcm47xx_bus.ssb, 1 << gpio, 0); +- return 0; +-#endif +-#ifdef CONFIG_BCM47XX_BCMA +- case BCM47XX_BUS_TYPE_BCMA: +- bcma_chipco_gpio_outen(&bcm47xx_bus.bcma.bus.drv_cc, 1 << gpio, +- 0); +- return 0; +-#endif +- } +- return -EINVAL; +-} ++int gpio_get_value(unsigned gpio); ++void gpio_set_value(unsigned gpio, int value); + +-static inline int gpio_direction_output(unsigned gpio, int value) ++static inline void gpio_intmask(unsigned gpio, int value) + { +- switch (bcm47xx_bus_type) { +-#ifdef CONFIG_BCM47XX_SSB +- case BCM47XX_BUS_TYPE_SSB: +- /* first set the gpio out value */ +- ssb_gpio_out(&bcm47xx_bus.ssb, 1 << gpio, +- value ? 1 << gpio : 0); +- /* then set the gpio mode */ +- ssb_gpio_outen(&bcm47xx_bus.ssb, 1 << gpio, 1 << gpio); +- return 0; +-#endif +-#ifdef CONFIG_BCM47XX_BCMA +- case BCM47XX_BUS_TYPE_BCMA: +- /* first set the gpio out value */ +- bcma_chipco_gpio_out(&bcm47xx_bus.bcma.bus.drv_cc, 1 << gpio, +- value ? 1 << gpio : 0); +- /* then set the gpio mode */ +- bcma_chipco_gpio_outen(&bcm47xx_bus.bcma.bus.drv_cc, 1 << gpio, +- 1 << gpio); +- return 0; +-#endif +- } +- return -EINVAL; ++ bcm47xx_gpio_intmask(1 << gpio, value ? 1 << gpio : 0); + } + +-static inline int gpio_intmask(unsigned gpio, int value) ++static inline void gpio_polarity(unsigned gpio, int value) + { +- switch (bcm47xx_bus_type) { +-#ifdef CONFIG_BCM47XX_SSB +- case BCM47XX_BUS_TYPE_SSB: +- ssb_gpio_intmask(&bcm47xx_bus.ssb, 1 << gpio, +- value ? 1 << gpio : 0); +- return 0; +-#endif +-#ifdef CONFIG_BCM47XX_BCMA +- case BCM47XX_BUS_TYPE_BCMA: +- bcma_chipco_gpio_intmask(&bcm47xx_bus.bcma.bus.drv_cc, +- 1 << gpio, value ? 1 << gpio : 0); +- return 0; +-#endif +- } +- return -EINVAL; ++ bcm47xx_gpio_polarity(1 << gpio, value ? 1 << gpio : 0); + } + +-static inline int gpio_polarity(unsigned gpio, int value) ++static inline int irq_to_gpio(int gpio) + { +- switch (bcm47xx_bus_type) { +-#ifdef CONFIG_BCM47XX_SSB +- case BCM47XX_BUS_TYPE_SSB: +- ssb_gpio_polarity(&bcm47xx_bus.ssb, 1 << gpio, +- value ? 1 << gpio : 0); +- return 0; +-#endif +-#ifdef CONFIG_BCM47XX_BCMA +- case BCM47XX_BUS_TYPE_BCMA: +- bcma_chipco_gpio_polarity(&bcm47xx_bus.bcma.bus.drv_cc, +- 1 << gpio, value ? 1 << gpio : 0); +- return 0; +-#endif +- } + return -EINVAL; + } + ++#define gpio_cansleep __gpio_cansleep ++#define gpio_to_irq __gpio_to_irq + + #endif /* __BCM47XX_GPIO_H */ diff --git a/target/linux/brcm47xx/patches-3.3/610-pci_ide_fix.patch b/target/linux/brcm47xx/patches-3.3/610-pci_ide_fix.patch new file mode 100644 index 000000000..f254b203b --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/610-pci_ide_fix.patch @@ -0,0 +1,14 @@ +--- a/include/linux/ide.h ++++ b/include/linux/ide.h +@@ -195,7 +195,11 @@ static inline void ide_std_init_ports(st + hw->io_ports.ctl_addr = ctl_addr; + } + ++#if defined CONFIG_BCM47XX ++# define MAX_HWIFS 2 ++#else + #define MAX_HWIFS 10 ++#endif + + /* + * Now for the data we need to maintain per-drive: ide_drive_t diff --git a/target/linux/brcm47xx/patches-3.3/700-ssb-gigabit-ethernet-driver.patch b/target/linux/brcm47xx/patches-3.3/700-ssb-gigabit-ethernet-driver.patch new file mode 100644 index 000000000..e40a621c0 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/700-ssb-gigabit-ethernet-driver.patch @@ -0,0 +1,439 @@ +--- a/drivers/net/ethernet/broadcom/tg3.c ++++ b/drivers/net/ethernet/broadcom/tg3.c +@@ -44,6 +44,7 @@ + #include <linux/prefetch.h> + #include <linux/dma-mapping.h> + #include <linux/firmware.h> ++#include <linux/ssb/ssb_driver_gige.h> + + #include <net/checksum.h> + #include <net/ip.h> +@@ -249,6 +250,7 @@ static DEFINE_PCI_DEVICE_TABLE(tg3_pci_t + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705F)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5721)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5722)}, ++ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751M)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751F)}, +@@ -530,7 +532,9 @@ static void _tw32_flush(struct tg3 *tp, + static inline void tw32_mailbox_flush(struct tg3 *tp, u32 off, u32 val) + { + tp->write32_mbox(tp, off, val); +- if (!tg3_flag(tp, MBOX_WRITE_REORDER) && !tg3_flag(tp, ICH_WORKAROUND)) ++ if (tg3_flag(tp, FLUSH_POSTED_WRITES) || ++ (!tg3_flag(tp, MBOX_WRITE_REORDER) && ++ !tg3_flag(tp, ICH_WORKAROUND))) + tp->read32_mbox(tp, off); + } + +@@ -540,7 +544,8 @@ static void tg3_write32_tx_mbox(struct t + writel(val, mbox); + if (tg3_flag(tp, TXD_MBOX_HWBUG)) + writel(val, mbox); +- if (tg3_flag(tp, MBOX_WRITE_REORDER)) ++ if (tg3_flag(tp, MBOX_WRITE_REORDER) || ++ tg3_flag(tp, FLUSH_POSTED_WRITES)) + readl(mbox); + } + +@@ -948,7 +953,8 @@ static void tg3_switch_clocks(struct tg3 + + #define PHY_BUSY_LOOPS 5000 + +-static int tg3_readphy(struct tg3 *tp, int reg, u32 *val) ++static int __tg3_readphy(struct tg3 *tp, unsigned int phy_addr, int reg, ++ u32 *val) + { + u32 frame_val; + unsigned int loops; +@@ -962,7 +968,7 @@ static int tg3_readphy(struct tg3 *tp, i + + *val = 0x0; + +- frame_val = ((tp->phy_addr << MI_COM_PHY_ADDR_SHIFT) & ++ frame_val = ((phy_addr << MI_COM_PHY_ADDR_SHIFT) & + MI_COM_PHY_ADDR_MASK); + frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) & + MI_COM_REG_ADDR_MASK); +@@ -997,7 +1003,13 @@ static int tg3_readphy(struct tg3 *tp, i + return ret; + } + +-static int tg3_writephy(struct tg3 *tp, int reg, u32 val) ++static int tg3_readphy(struct tg3 *tp, int reg, u32 *val) ++{ ++ return __tg3_readphy(tp, tp->phy_addr, reg, val); ++} ++ ++static int __tg3_writephy(struct tg3 *tp, unsigned int phy_addr, int reg, ++ u32 val) + { + u32 frame_val; + unsigned int loops; +@@ -1013,7 +1025,7 @@ static int tg3_writephy(struct tg3 *tp, + udelay(80); + } + +- frame_val = ((tp->phy_addr << MI_COM_PHY_ADDR_SHIFT) & ++ frame_val = ((phy_addr << MI_COM_PHY_ADDR_SHIFT) & + MI_COM_PHY_ADDR_MASK); + frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) & + MI_COM_REG_ADDR_MASK); +@@ -1046,6 +1058,11 @@ static int tg3_writephy(struct tg3 *tp, + return ret; + } + ++static int tg3_writephy(struct tg3 *tp, int reg, u32 val) ++{ ++ return __tg3_writephy(tp, tp->phy_addr, reg, val); ++} ++ + static int tg3_phy_cl45_write(struct tg3 *tp, u32 devad, u32 addr, u32 val) + { + int err; +@@ -1608,6 +1625,11 @@ static int tg3_poll_fw(struct tg3 *tp) + int i; + u32 val; + ++ if (tg3_flag(tp, IS_SSB_CORE)) { ++ /* We don't use firmware. */ ++ return 0; ++ } ++ + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { + /* Wait up to 20ms for init done. */ + for (i = 0; i < 200; i++) { +@@ -3015,6 +3037,11 @@ static int tg3_halt_cpu(struct tg3 *tp, + tw32_f(offset + CPU_MODE, CPU_MODE_HALT); + udelay(10); + } else { ++ /* There is only an Rx CPU for the 5750 derivative in the ++ * BCM4785. */ ++ if (tg3_flag(tp, IS_SSB_CORE)) ++ return 0; ++ + for (i = 0; i < 10000; i++) { + tw32(offset + CPU_STATE, 0xffffffff); + tw32(offset + CPU_MODE, CPU_MODE_HALT); +@@ -3029,9 +3056,12 @@ static int tg3_halt_cpu(struct tg3 *tp, + return -ENODEV; + } + +- /* Clear firmware's nvram arbitration. */ +- if (tg3_flag(tp, NVRAM)) +- tw32(NVRAM_SWARB, SWARB_REQ_CLR0); ++ if (!tg3_flag(tp, IS_SSB_CORE)) { ++ /* Clear firmware's nvram arbitration. */ ++ if (tg3_flag(tp, NVRAM)) ++ tw32(NVRAM_SWARB, SWARB_REQ_CLR0); ++ } ++ + return 0; + } + +@@ -3094,6 +3124,11 @@ static int tg3_load_5701_a0_firmware_fix + const __be32 *fw_data; + int err, i; + ++ if (tg3_flag(tp, IS_SSB_CORE)) { ++ /* We don't use firmware. */ ++ return 0; ++ } ++ + fw_data = (void *)tp->fw->data; + + /* Firmware blob starts with version numbers, followed by +@@ -3150,6 +3185,11 @@ static int tg3_load_tso_firmware(struct + unsigned long cpu_base, cpu_scratch_base, cpu_scratch_size; + int err, i; + ++ if (tg3_flag(tp, IS_SSB_CORE)) { ++ /* We don't use firmware. */ ++ return 0; ++ } ++ + if (tg3_flag(tp, HW_TSO_1) || + tg3_flag(tp, HW_TSO_2) || + tg3_flag(tp, HW_TSO_3)) +@@ -3496,8 +3536,9 @@ static int tg3_power_down_prepare(struct + tg3_frob_aux_power(tp, true); + + /* Workaround for unstable PLL clock */ +- if ((GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5750_AX) || +- (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5750_BX)) { ++ if ((!tg3_flag(tp, IS_SSB_CORE)) && ++ ((GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5750_AX) || ++ (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5750_BX))) { + u32 val = tr32(0x7d00); + + val &= ~((1 << 16) | (1 << 4) | (1 << 2) | (1 << 1) | 1); +@@ -4011,6 +4052,14 @@ relink: + if (current_link_up == 0 || (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER)) { + tg3_phy_copper_begin(tp); + ++ if (tg3_flag(tp, ROBOSWITCH)) { ++ current_link_up = 1; ++ current_speed = SPEED_1000; /* FIXME */ ++ current_duplex = DUPLEX_FULL; ++ tp->link_config.active_speed = current_speed; ++ tp->link_config.active_duplex = current_duplex; ++ } ++ + tg3_readphy(tp, MII_BMSR, &bmsr); + if ((!tg3_readphy(tp, MII_BMSR, &bmsr) && (bmsr & BMSR_LSTATUS)) || + (tp->mac_mode & MAC_MODE_PORT_INT_LPBACK)) +@@ -4029,6 +4078,26 @@ relink: + else + tp->mac_mode |= MAC_MODE_PORT_MODE_GMII; + ++ /* In order for the 5750 core in BCM4785 chip to work properly ++ * in RGMII mode, the Led Control Register must be set up. ++ */ ++ if (tg3_flag(tp, RGMII_MODE)) { ++ u32 led_ctrl = tr32(MAC_LED_CTRL); ++ led_ctrl &= ~(LED_CTRL_1000MBPS_ON | LED_CTRL_100MBPS_ON); ++ ++ if (tp->link_config.active_speed == SPEED_10) ++ led_ctrl |= LED_CTRL_LNKLED_OVERRIDE; ++ else if (tp->link_config.active_speed == SPEED_100) ++ led_ctrl |= (LED_CTRL_LNKLED_OVERRIDE | ++ LED_CTRL_100MBPS_ON); ++ else if (tp->link_config.active_speed == SPEED_1000) ++ led_ctrl |= (LED_CTRL_LNKLED_OVERRIDE | ++ LED_CTRL_1000MBPS_ON); ++ ++ tw32(MAC_LED_CTRL, led_ctrl); ++ udelay(40); ++ } ++ + tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX; + if (tp->link_config.active_duplex == DUPLEX_HALF) + tp->mac_mode |= MAC_MODE_HALF_DUPLEX; +@@ -7833,6 +7902,14 @@ static int tg3_chip_reset(struct tg3 *tp + tw32(0x5000, 0x400); + } + ++ if (tg3_flag(tp, IS_SSB_CORE)) { ++ /* BCM4785: In order to avoid repercussions from using ++ * potentially defective internal ROM, stop the Rx RISC CPU, ++ * which is not required. */ ++ tg3_stop_fw(tp); ++ tg3_halt_cpu(tp, RX_CPU_BASE); ++ } ++ + tw32(GRC_MODE, tp->grc_mode); + + if (tp->pci_chip_rev_id == CHIPREV_ID_5705_A0) { +@@ -9247,6 +9324,11 @@ static void tg3_timer(unsigned long __op + tg3_flag(tp, 57765_CLASS)) + tg3_chk_missed_msi(tp); + ++ if (tg3_flag(tp, FLUSH_POSTED_WRITES)) { ++ /* BCM4785: Flush posted writes from GbE to host memory. */ ++ tr32(HOSTCC_MODE); ++ } ++ + if (!tg3_flag(tp, TAGGED_STATUS)) { + /* All of this garbage is because when using non-tagged + * IRQ status the mailbox/status_block protocol the chip +@@ -10959,6 +11041,11 @@ static int tg3_test_nvram(struct tg3 *tp + if (tg3_flag(tp, NO_NVRAM)) + return 0; + ++ if (tg3_flag(tp, IS_SSB_CORE)) { ++ /* We don't have NVRAM. */ ++ return 0; ++ } ++ + if (tg3_nvram_read(tp, 0, &magic) != 0) + return -EIO; + +@@ -11916,11 +12003,12 @@ static int tg3_ioctl(struct net_device * + if (tp->phy_flags & TG3_PHYFLG_PHY_SERDES) + break; /* We have no PHY */ + +- if (!netif_running(dev)) ++ if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) + return -EAGAIN; + + spin_lock_bh(&tp->lock); +- err = tg3_readphy(tp, data->reg_num & 0x1f, &mii_regval); ++ err = __tg3_readphy(tp, data->phy_id & 0x1f, ++ data->reg_num & 0x1f, &mii_regval); + spin_unlock_bh(&tp->lock); + + data->val_out = mii_regval; +@@ -11932,11 +12020,12 @@ static int tg3_ioctl(struct net_device * + if (tp->phy_flags & TG3_PHYFLG_PHY_SERDES) + break; /* We have no PHY */ + +- if (!netif_running(dev)) ++ if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) + return -EAGAIN; + + spin_lock_bh(&tp->lock); +- err = tg3_writephy(tp, data->reg_num & 0x1f, data->val_in); ++ err = __tg3_writephy(tp, data->phy_id & 0x1f, ++ data->reg_num & 0x1f, data->val_in); + spin_unlock_bh(&tp->lock); + + return err; +@@ -12670,6 +12759,13 @@ static void __devinit tg3_get_5720_nvram + /* Chips other than 5700/5701 use the NVRAM for fetching info. */ + static void __devinit tg3_nvram_init(struct tg3 *tp) + { ++ if (tg3_flag(tp, IS_SSB_CORE)) { ++ /* No NVRAM and EEPROM on the SSB Broadcom GigE core. */ ++ tg3_flag_clear(tp, NVRAM); ++ tg3_flag_clear(tp, NVRAM_BUFFERED); ++ return; ++ } ++ + tw32_f(GRC_EEPROM_ADDR, + (EEPROM_ADDR_FSM_RESET | + (EEPROM_DEFAULT_CLOCK_PERIOD << +@@ -12936,6 +13032,9 @@ static int tg3_nvram_write_block(struct + { + int ret; + ++ if (tg3_flag(tp, IS_SSB_CORE)) ++ return -ENODEV; ++ + if (tg3_flag(tp, EEPROM_WRITE_PROT)) { + tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl & + ~GRC_LCLCTRL_GPIO_OUTPUT1); +@@ -13394,10 +13493,19 @@ static int __devinit tg3_phy_probe(struc + * subsys device table. + */ + p = tg3_lookup_by_subsys(tp); +- if (!p) ++ if (p) { ++ tp->phy_id = p->phy_id; ++ } else if (!tg3_flag(tp, IS_SSB_CORE)) { ++ /* For now we saw the IDs 0xbc050cd0, ++ * 0xbc050f80 and 0xbc050c30 on devices ++ * connected to an BCM4785 and there are ++ * probably more. Just assume that the phy is ++ * supported when it is connected to a SSB core ++ * for now. ++ */ + return -ENODEV; ++ } + +- tp->phy_id = p->phy_id; + if (!tp->phy_id || + tp->phy_id == TG3_PHY_ID_BCM8002) + tp->phy_flags |= TG3_PHYFLG_PHY_SERDES; +@@ -14382,6 +14490,11 @@ static int __devinit tg3_get_invariants( + } + } + ++ if (tg3_flag(tp, FLUSH_POSTED_WRITES)) { ++ tp->write32_tx_mbox = tg3_write_flush_reg32; ++ tp->write32_rx_mbox = tg3_write_flush_reg32; ++ } ++ + /* Get eeprom hw config before calling tg3_set_power_state(). + * In particular, the TG3_FLAG_IS_NIC flag must be + * determined before calling tg3_set_power_state() so that +@@ -14798,6 +14911,10 @@ static int __devinit tg3_get_device_addr + } + + if (!is_valid_ether_addr(&dev->dev_addr[0])) { ++ if (tg3_flag(tp, IS_SSB_CORE)) ++ ssb_gige_get_macaddr(tp->pdev, &dev->dev_addr[0]); ++ } ++ if (!is_valid_ether_addr(&dev->dev_addr[0])) { + #ifdef CONFIG_SPARC + if (!tg3_get_default_macaddr_sparc(tp)) + return 0; +@@ -15082,7 +15199,8 @@ static int __devinit tg3_test_dma(struct + if (tg3_flag(tp, 40BIT_DMA_BUG) && + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) + tp->dma_rwctrl |= 0x8000; +- else if (ccval == 0x6 || ccval == 0x7) ++ else if ((ccval == 0x6 || ccval == 0x7) || ++ tg3_flag(tp, ONE_DMA_AT_ONCE)) + tp->dma_rwctrl |= DMA_RWCTRL_ONE_DMA; + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) +@@ -15506,6 +15624,17 @@ static int __devinit tg3_init_one(struct + tp->msg_enable = tg3_debug; + else + tp->msg_enable = TG3_DEF_MSG_ENABLE; ++ if (pdev_is_ssb_gige_core(pdev)) { ++ tg3_flag_set(tp, IS_SSB_CORE); ++ if (ssb_gige_must_flush_posted_writes(pdev)) ++ tg3_flag_set(tp, FLUSH_POSTED_WRITES); ++ if (ssb_gige_one_dma_at_once(pdev)) ++ tg3_flag_set(tp, ONE_DMA_AT_ONCE); ++ if (ssb_gige_have_roboswitch(pdev)) ++ tg3_flag_set(tp, ROBOSWITCH); ++ if (ssb_gige_is_rgmii(pdev)) ++ tg3_flag_set(tp, RGMII_MODE); ++ } + + /* The word/byte swap controls here control register access byte + * swapping. DMA data byte swapping is controlled in the GRC_MODE +--- a/drivers/net/ethernet/broadcom/tg3.h ++++ b/drivers/net/ethernet/broadcom/tg3.h +@@ -2940,6 +2940,11 @@ enum TG3_FLAGS { + TG3_FLAG_57765_PLUS, + TG3_FLAG_57765_CLASS, + TG3_FLAG_5717_PLUS, ++ TG3_FLAG_IS_SSB_CORE, ++ TG3_FLAG_FLUSH_POSTED_WRITES, ++ TG3_FLAG_ROBOSWITCH, ++ TG3_FLAG_ONE_DMA_AT_ONCE, ++ TG3_FLAG_RGMII_MODE, + + /* Add new flags before this comment and TG3_FLAG_NUMBER_OF_FLAGS */ + TG3_FLAG_NUMBER_OF_FLAGS, /* Last entry in enum TG3_FLAGS */ +--- a/include/linux/pci_ids.h ++++ b/include/linux/pci_ids.h +@@ -2120,6 +2120,7 @@ + #define PCI_DEVICE_ID_TIGON3_5754M 0x1672 + #define PCI_DEVICE_ID_TIGON3_5755M 0x1673 + #define PCI_DEVICE_ID_TIGON3_5756 0x1674 ++#define PCI_DEVICE_ID_TIGON3_5750 0x1676 + #define PCI_DEVICE_ID_TIGON3_5751 0x1677 + #define PCI_DEVICE_ID_TIGON3_5715 0x1678 + #define PCI_DEVICE_ID_TIGON3_5715S 0x1679 +--- a/include/linux/ssb/ssb_driver_gige.h ++++ b/include/linux/ssb/ssb_driver_gige.h +@@ -97,21 +97,12 @@ static inline bool ssb_gige_must_flush_p + return 0; + } + +-#ifdef CONFIG_BCM47XX +-#include <asm/mach-bcm47xx/nvram.h> +-/* Get the device MAC address */ +-static inline void ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr) +-{ +- char buf[20]; +- if (nvram_getenv("et0macaddr", buf, sizeof(buf)) < 0) +- return; +- nvram_parse_macaddr(buf, macaddr); +-} +-#else + static inline void ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr) + { ++ struct ssb_gige *dev = pdev_to_ssb_gige(pdev); ++ ++ memcpy(macaddr, dev->dev->bus->sprom.et0mac, 6); + } +-#endif + + extern int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev, + struct pci_dev *pdev); +@@ -175,6 +166,9 @@ static inline bool ssb_gige_must_flush_p + { + return 0; + } ++static inline void ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr) ++{ ++} + + #endif /* CONFIG_SSB_DRIVER_GIGE */ + #endif /* LINUX_SSB_DRIVER_GIGE_H_ */ diff --git a/target/linux/brcm47xx/patches-3.3/812-disable_wgt634u_crap.patch b/target/linux/brcm47xx/patches-3.3/812-disable_wgt634u_crap.patch new file mode 100644 index 000000000..a2902e7c7 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/812-disable_wgt634u_crap.patch @@ -0,0 +1,187 @@ +--- a/arch/mips/bcm47xx/Makefile ++++ b/arch/mips/bcm47xx/Makefile +@@ -4,4 +4,3 @@ + # + + obj-y += gpio.o irq.o nvram.o prom.o serial.o setup.o time.o sprom.o bus.o +-obj-$(CONFIG_BCM47XX_SSB) += wgt634u.o +--- a/arch/mips/bcm47xx/wgt634u.c ++++ /dev/null +@@ -1,177 +0,0 @@ +-/* +- * This file is subject to the terms and conditions of the GNU General Public +- * License. See the file "COPYING" in the main directory of this archive +- * for more details. +- * +- * Copyright (C) 2007 Aurelien Jarno <aurelien@aurel32.net> +- */ +- +-#include <linux/platform_device.h> +-#include <linux/module.h> +-#include <linux/leds.h> +-#include <linux/mtd/physmap.h> +-#include <linux/ssb/ssb.h> +-#include <linux/interrupt.h> +-#include <linux/reboot.h> +-#include <linux/gpio.h> +-#include <asm/mach-bcm47xx/bcm47xx.h> +- +-/* GPIO definitions for the WGT634U */ +-#define WGT634U_GPIO_LED 3 +-#define WGT634U_GPIO_RESET 2 +-#define WGT634U_GPIO_TP1 7 +-#define WGT634U_GPIO_TP2 6 +-#define WGT634U_GPIO_TP3 5 +-#define WGT634U_GPIO_TP4 4 +-#define WGT634U_GPIO_TP5 1 +- +-static struct gpio_led wgt634u_leds[] = { +- { +- .name = "power", +- .gpio = WGT634U_GPIO_LED, +- .active_low = 1, +- .default_trigger = "heartbeat", +- }, +-}; +- +-static struct gpio_led_platform_data wgt634u_led_data = { +- .num_leds = ARRAY_SIZE(wgt634u_leds), +- .leds = wgt634u_leds, +-}; +- +-static struct platform_device wgt634u_gpio_leds = { +- .name = "leds-gpio", +- .id = -1, +- .dev = { +- .platform_data = &wgt634u_led_data, +- } +-}; +- +- +-/* 8MiB flash. The struct mtd_partition matches original Netgear WGT634U +- firmware. */ +-static struct mtd_partition wgt634u_partitions[] = { +- { +- .name = "cfe", +- .offset = 0, +- .size = 0x60000, /* 384k */ +- .mask_flags = MTD_WRITEABLE /* force read-only */ +- }, +- { +- .name = "config", +- .offset = 0x60000, +- .size = 0x20000 /* 128k */ +- }, +- { +- .name = "linux", +- .offset = 0x80000, +- .size = 0x140000 /* 1280k */ +- }, +- { +- .name = "jffs", +- .offset = 0x1c0000, +- .size = 0x620000 /* 6272k */ +- }, +- { +- .name = "nvram", +- .offset = 0x7e0000, +- .size = 0x20000 /* 128k */ +- }, +-}; +- +-static struct physmap_flash_data wgt634u_flash_data = { +- .parts = wgt634u_partitions, +- .nr_parts = ARRAY_SIZE(wgt634u_partitions) +-}; +- +-static struct resource wgt634u_flash_resource = { +- .flags = IORESOURCE_MEM, +-}; +- +-static struct platform_device wgt634u_flash = { +- .name = "physmap-flash", +- .id = 0, +- .dev = { .platform_data = &wgt634u_flash_data, }, +- .resource = &wgt634u_flash_resource, +- .num_resources = 1, +-}; +- +-/* Platform devices */ +-static struct platform_device *wgt634u_devices[] __initdata = { +- &wgt634u_flash, +- &wgt634u_gpio_leds, +-}; +- +-static irqreturn_t gpio_interrupt(int irq, void *ignored) +-{ +- int state; +- +- /* Interrupts are shared, check if the current one is +- a GPIO interrupt. */ +- if (!ssb_chipco_irq_status(&bcm47xx_bus.ssb.chipco, +- SSB_CHIPCO_IRQ_GPIO)) +- return IRQ_NONE; +- +- state = gpio_get_value(WGT634U_GPIO_RESET); +- +- /* Interrupt are level triggered, revert the interrupt polarity +- to clear the interrupt. */ +- gpio_polarity(WGT634U_GPIO_RESET, state); +- +- if (!state) { +- printk(KERN_INFO "Reset button pressed"); +- ctrl_alt_del(); +- } +- +- return IRQ_HANDLED; +-} +- +-static int __init wgt634u_init(void) +-{ +- /* There is no easy way to detect that we are running on a WGT634U +- * machine. Use the MAC address as an heuristic. Netgear Inc. has +- * been allocated ranges 00:09:5b:xx:xx:xx and 00:0f:b5:xx:xx:xx. +- */ +- u8 *et0mac; +- int err; +- +- if (bcm47xx_bus_type != BCM47XX_BUS_TYPE_SSB) +- return -ENODEV; +- +- et0mac = bcm47xx_bus.ssb.sprom.et0mac; +- +- if (et0mac[0] == 0x00 && +- ((et0mac[1] == 0x09 && et0mac[2] == 0x5b) || +- (et0mac[1] == 0x0f && et0mac[2] == 0xb5))) { +- struct ssb_chipcommon *ccore = &bcm47xx_bus.ssb.chipco; +- +- printk(KERN_INFO "WGT634U machine detected.\n"); +- +- err = gpio_request(WGT634U_GPIO_RESET, "reset-buton"); +- if (err) { +- printk(KERN_INFO "Can not register gpio for reset button\n"); +- return 0; +- } +- +- if (!request_irq(gpio_to_irq(WGT634U_GPIO_RESET), +- gpio_interrupt, IRQF_SHARED, +- "WGT634U GPIO", ccore)) { +- gpio_direction_input(WGT634U_GPIO_RESET); +- gpio_intmask(WGT634U_GPIO_RESET, 1); +- ssb_chipco_irq_mask(ccore, +- SSB_CHIPCO_IRQ_GPIO, +- SSB_CHIPCO_IRQ_GPIO); +- } +- +- wgt634u_flash_data.width = ccore->pflash.buswidth; +- wgt634u_flash_resource.start = ccore->pflash.window; +- wgt634u_flash_resource.end = ccore->pflash.window +- + ccore->pflash.window_size +- - 1; +- return platform_add_devices(wgt634u_devices, +- ARRAY_SIZE(wgt634u_devices)); +- } else +- return -ENODEV; +-} +- +-module_init(wgt634u_init); diff --git a/target/linux/brcm47xx/patches-3.3/820-wgt634u-nvram-fix.patch b/target/linux/brcm47xx/patches-3.3/820-wgt634u-nvram-fix.patch new file mode 100644 index 000000000..457958a9d --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/820-wgt634u-nvram-fix.patch @@ -0,0 +1,305 @@ +The Netgear wgt634u uses a different format for storing the +configuration. This patch is needed to read out the correct +configuration. The cfe_env.c file uses a different method way to read +out the configuration than the in kernel cfe config reader. + +--- a/arch/mips/bcm47xx/Makefile ++++ b/arch/mips/bcm47xx/Makefile +@@ -3,4 +3,4 @@ + # under Linux. + # + +-obj-y += gpio.o irq.o nvram.o prom.o serial.o setup.o time.o sprom.o bus.o ++obj-y += gpio.o irq.o nvram.o prom.o serial.o setup.o time.o sprom.o bus.o cfe_env.o +--- /dev/null ++++ b/arch/mips/bcm47xx/cfe_env.c +@@ -0,0 +1,229 @@ ++/* ++ * CFE environment variable access ++ * ++ * Copyright 2001-2003, Broadcom Corporation ++ * Copyright 2006, Felix Fietkau <nbd@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/string.h> ++#include <asm/io.h> ++#include <asm/uaccess.h> ++ ++#define NVRAM_SIZE (0x1ff0) ++static char _nvdata[NVRAM_SIZE]; ++static char _valuestr[256]; ++ ++/* ++ * TLV types. These codes are used in the "type-length-value" ++ * encoding of the items stored in the NVRAM device (flash or EEPROM) ++ * ++ * The layout of the flash/nvram is as follows: ++ * ++ * <type> <length> <data ...> <type> <length> <data ...> <type_end> ++ * ++ * The type code of "ENV_TLV_TYPE_END" marks the end of the list. ++ * The "length" field marks the length of the data section, not ++ * including the type and length fields. ++ * ++ * Environment variables are stored as follows: ++ * ++ * <type_env> <length> <flags> <name> = <value> ++ * ++ * If bit 0 (low bit) is set, the length is an 8-bit value. ++ * If bit 0 (low bit) is clear, the length is a 16-bit value ++ * ++ * Bit 7 set indicates "user" TLVs. In this case, bit 0 still ++ * indicates the size of the length field. ++ * ++ * Flags are from the constants below: ++ * ++ */ ++#define ENV_LENGTH_16BITS 0x00 /* for low bit */ ++#define ENV_LENGTH_8BITS 0x01 ++ ++#define ENV_TYPE_USER 0x80 ++ ++#define ENV_CODE_SYS(n,l) (((n)<<1)|(l)) ++#define ENV_CODE_USER(n,l) ((((n)<<1)|(l)) | ENV_TYPE_USER) ++ ++/* ++ * The actual TLV types we support ++ */ ++ ++#define ENV_TLV_TYPE_END 0x00 ++#define ENV_TLV_TYPE_ENV ENV_CODE_SYS(0,ENV_LENGTH_8BITS) ++ ++/* ++ * Environment variable flags ++ */ ++ ++#define ENV_FLG_NORMAL 0x00 /* normal read/write */ ++#define ENV_FLG_BUILTIN 0x01 /* builtin - not stored in flash */ ++#define ENV_FLG_READONLY 0x02 /* read-only - cannot be changed */ ++ ++#define ENV_FLG_MASK 0xFF /* mask of attributes we keep */ ++#define ENV_FLG_ADMIN 0x100 /* lets us internally override permissions */ ++ ++ ++/* ********************************************************************* ++ * _nvram_read(buffer,offset,length) ++ * ++ * Read data from the NVRAM device ++ * ++ * Input parameters: ++ * buffer - destination buffer ++ * offset - offset of data to read ++ * length - number of bytes to read ++ * ++ * Return value: ++ * number of bytes read, or <0 if error occured ++ ********************************************************************* */ ++static int ++_nvram_read(unsigned char *nv_buf, unsigned char *buffer, int offset, int length) ++{ ++ int i; ++ if (offset > NVRAM_SIZE) ++ return -1; ++ ++ for ( i = 0; i < length; i++) { ++ buffer[i] = ((volatile unsigned char*)nv_buf)[offset + i]; ++ } ++ return length; ++} ++ ++ ++static char* ++_strnchr(const char *dest,int c,size_t cnt) ++{ ++ while (*dest && (cnt > 0)) { ++ if (*dest == c) return (char *) dest; ++ dest++; ++ cnt--; ++ } ++ return NULL; ++} ++ ++ ++ ++/* ++ * Core support API: Externally visible. ++ */ ++ ++/* ++ * Get the value of an NVRAM variable ++ * @param name name of variable to get ++ * @return value of variable or NULL if undefined ++ */ ++ ++char* ++cfe_env_get(unsigned char *nv_buf, char* name) ++{ ++ int size; ++ unsigned char *buffer; ++ unsigned char *ptr; ++ unsigned char *envval; ++ unsigned int reclen; ++ unsigned int rectype; ++ int offset; ++ int flg; ++ ++ if (!strcmp(name, "nvram_type")) ++ return "cfe"; ++ ++ size = NVRAM_SIZE; ++ buffer = &_nvdata[0]; ++ ++ ptr = buffer; ++ offset = 0; ++ ++ /* Read the record type and length */ ++ if (_nvram_read(nv_buf, ptr,offset,1) != 1) { ++ goto error; ++ } ++ ++ while ((*ptr != ENV_TLV_TYPE_END) && (size > 1)) { ++ ++ /* Adjust pointer for TLV type */ ++ rectype = *(ptr); ++ offset++; ++ size--; ++ ++ /* ++ * Read the length. It can be either 1 or 2 bytes ++ * depending on the code ++ */ ++ if (rectype & ENV_LENGTH_8BITS) { ++ /* Read the record type and length - 8 bits */ ++ if (_nvram_read(nv_buf, ptr,offset,1) != 1) { ++ goto error; ++ } ++ reclen = *(ptr); ++ size--; ++ offset++; ++ } ++ else { ++ /* Read the record type and length - 16 bits, MSB first */ ++ if (_nvram_read(nv_buf, ptr,offset,2) != 2) { ++ goto error; ++ } ++ reclen = (((unsigned int) *(ptr)) << 8) + (unsigned int) *(ptr+1); ++ size -= 2; ++ offset += 2; ++ } ++ ++ if (reclen > size) ++ break; /* should not happen, bad NVRAM */ ++ ++ switch (rectype) { ++ case ENV_TLV_TYPE_ENV: ++ /* Read the TLV data */ ++ if (_nvram_read(nv_buf, ptr,offset,reclen) != reclen) ++ goto error; ++ flg = *ptr++; ++ envval = (unsigned char *) _strnchr(ptr,'=',(reclen-1)); ++ if (envval) { ++ *envval++ = '\0'; ++ memcpy(_valuestr,envval,(reclen-1)-(envval-ptr)); ++ _valuestr[(reclen-1)-(envval-ptr)] = '\0'; ++#if 0 ++ printk(KERN_INFO "NVRAM:%s=%s\n", ptr, _valuestr); ++#endif ++ if(!strcmp(ptr, name)){ ++ return _valuestr; ++ } ++ if((strlen(ptr) > 1) && !strcmp(&ptr[1], name)) ++ return _valuestr; ++ } ++ break; ++ ++ default: ++ /* Unknown TLV type, skip it. */ ++ break; ++ } ++ ++ /* ++ * Advance to next TLV ++ */ ++ ++ size -= (int)reclen; ++ offset += reclen; ++ ++ /* Read the next record type */ ++ ptr = buffer; ++ if (_nvram_read(nv_buf, ptr,offset,1) != 1) ++ goto error; ++ } ++ ++error: ++ return NULL; ++ ++} ++ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -25,6 +25,8 @@ + #include <linux/mtd/bcm47xx_nand.h> + + static char nvram_buf[NVRAM_SPACE]; ++static int cfe_env; ++extern char *cfe_env_get(char *nv_buf, const char *name); + + /* Probe for NVRAM header */ + static void early_nvram_init_pflash(void) +@@ -58,6 +60,25 @@ static void early_nvram_init_pflash(void + break; + #endif + } ++ cfe_env = 0; ++ ++ /* XXX: hack for supporting the CFE environment stuff on WGT634U */ ++ if (lim >= 8 * 1024 * 1024) { ++ src = (u32 *) KSEG1ADDR(base + 8 * 1024 * 1024 - 0x2000); ++ dst = (u32 *) nvram_buf; ++ ++ if ((*src & 0xff00ff) == 0x000001) { ++ printk("early_nvram_init: WGT634U NVRAM found.\n"); ++ ++ for (i = 0; i < 0x1ff0; i++) { ++ if (*src == 0xFFFFFFFF) ++ break; ++ *dst++ = *src++; ++ } ++ cfe_env = 1; ++ return; ++ } ++ } + + off = FLASH_MIN; + while (off <= lim) { +@@ -257,6 +278,12 @@ int nvram_getenv(char *name, char *val, + if (!nvram_buf[0]) + early_nvram_init(); + ++ if (cfe_env) { ++ value = cfe_env_get(nvram_buf, name); ++ snprintf(val, val_len, "%s", value); ++ return 0; ++ } ++ + /* Look for name=value and return value */ + var = &nvram_buf[sizeof(struct nvram_header)]; + end = nvram_buf + sizeof(nvram_buf) - 2; +@@ -285,6 +312,9 @@ char *nvram_get(const char *name) + if (!nvram_buf[0]) + early_nvram_init(); + ++ if (cfe_env) ++ return cfe_env_get(nvram_buf, name); ++ + /* Look for name=value and return value */ + var = &nvram_buf[sizeof(struct nvram_header)]; + end = nvram_buf + sizeof(nvram_buf) - 2; diff --git a/target/linux/brcm47xx/patches-3.3/900-bcm47xx_wdt-noprescale.patch b/target/linux/brcm47xx/patches-3.3/900-bcm47xx_wdt-noprescale.patch new file mode 100644 index 000000000..d60247e91 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/900-bcm47xx_wdt-noprescale.patch @@ -0,0 +1,104 @@ +--- a/drivers/watchdog/bcm47xx_wdt.c ++++ b/drivers/watchdog/bcm47xx_wdt.c +@@ -31,6 +31,7 @@ + + #define WDT_DEFAULT_TIME 30 /* seconds */ + #define WDT_MAX_TIME 255 /* seconds */ ++#define WDT_SHIFT 15 /* 32.768 KHz on cores with slow WDT clock */ + + static int wdt_time = WDT_DEFAULT_TIME; + static int nowayout = WATCHDOG_NOWAYOUT; +@@ -50,20 +51,20 @@ static unsigned long bcm47xx_wdt_busy; + static char expect_release; + static struct timer_list wdt_timer; + static atomic_t ticks; ++static int needs_sw_scale; + +-static inline void bcm47xx_wdt_hw_start(void) ++static inline void bcm47xx_wdt_hw_start(u32 ticks) + { +- /* this is 2,5s on 100Mhz clock and 2s on 133 Mhz */ + switch (bcm47xx_bus_type) { + #ifdef CONFIG_BCM47XX_SSB + case BCM47XX_BUS_TYPE_SSB: +- ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 0xfffffff); ++ ssb_watchdog_timer_set(&bcm47xx_bus.ssb, ticks); + break; + #endif + #ifdef CONFIG_BCM47XX_BCMA + case BCM47XX_BUS_TYPE_BCMA: + bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc, +- 0xfffffff); ++ ticks); + break; + #endif + } +@@ -88,33 +89,34 @@ static inline int bcm47xx_wdt_hw_stop(vo + static void bcm47xx_timer_tick(unsigned long unused) + { + if (!atomic_dec_and_test(&ticks)) { +- bcm47xx_wdt_hw_start(); ++ /* This is 2,5s on 100Mhz clock and 2s on 133 Mhz */ ++ bcm47xx_wdt_hw_start(0xfffffff); + mod_timer(&wdt_timer, jiffies + HZ); + } else { +- printk(KERN_CRIT DRV_NAME "Watchdog will fire soon!!!\n"); ++ printk(KERN_CRIT DRV_NAME ": Watchdog will fire soon!!!\n"); + } + } + +-static inline void bcm47xx_wdt_pet(void) ++static void bcm47xx_wdt_pet(void) + { +- atomic_set(&ticks, wdt_time); ++ if(needs_sw_scale) ++ atomic_set(&ticks, wdt_time); ++ else ++ bcm47xx_wdt_hw_start(wdt_time << WDT_SHIFT); + } + + static void bcm47xx_wdt_start(void) + { + bcm47xx_wdt_pet(); +- bcm47xx_timer_tick(0); +-} +- +-static void bcm47xx_wdt_pause(void) +-{ +- del_timer_sync(&wdt_timer); +- bcm47xx_wdt_hw_stop(); ++ if(needs_sw_scale) ++ bcm47xx_timer_tick(0); + } + + static void bcm47xx_wdt_stop(void) + { +- bcm47xx_wdt_pause(); ++ if(needs_sw_scale) ++ del_timer_sync(&wdt_timer); ++ bcm47xx_wdt_hw_stop(); + } + + static int bcm47xx_wdt_settimeout(int new_time) +@@ -266,7 +268,20 @@ static int __init bcm47xx_wdt_init(void) + if (bcm47xx_wdt_hw_stop() < 0) + return -ENODEV; + +- setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L); ++ /* FIXME Other cores */ ++#ifdef BCM47XX_BUS_TYPE_BCMA ++ if(bcm47xx_bus_type == BCM47XX_BUS_TYPE_BCMA && ++ bcm47xx_bus.ssb.chip_id == 0x5354) { ++ /* Slow WDT clock, no pre-scaling */ ++ needs_sw_scale = 0; ++ } else { ++#endif ++ /* Fast WDT clock, needs software pre-scaling */ ++ needs_sw_scale = 1; ++ setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L); ++#ifdef BCM47XX_BUS_TYPE_BCMA ++ } ++#endif + + if (bcm47xx_wdt_settimeout(wdt_time)) { + bcm47xx_wdt_settimeout(WDT_DEFAULT_TIME); diff --git a/target/linux/brcm47xx/patches-3.3/920-cache-wround.patch b/target/linux/brcm47xx/patches-3.3/920-cache-wround.patch new file mode 100644 index 000000000..0f6f76596 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/920-cache-wround.patch @@ -0,0 +1,138 @@ +--- a/arch/mips/include/asm/r4kcache.h ++++ b/arch/mips/include/asm/r4kcache.h +@@ -20,10 +20,28 @@ + #ifdef CONFIG_BCM47XX + #include <asm/paccess.h> + #include <linux/ssb/ssb.h> +-#define BCM4710_DUMMY_RREG() ((void) *((u8 *) KSEG1ADDR(SSB_ENUM_BASE))) ++#define BCM4710_DUMMY_RREG() bcm4710_dummy_rreg() ++ ++static inline unsigned long bcm4710_dummy_rreg(void) ++{ ++ return *(volatile unsigned long *)(KSEG1ADDR(SSB_ENUM_BASE)); ++} ++ ++#define BCM4710_FILL_TLB(addr) bcm4710_fill_tlb((void *)(addr)) ++ ++static inline unsigned long bcm4710_fill_tlb(void *addr) ++{ ++ return *(unsigned long *)addr; ++} ++ ++#define BCM4710_PROTECTED_FILL_TLB(addr) bcm4710_protected_fill_tlb((void *)(addr)) ++ ++static inline void bcm4710_protected_fill_tlb(void *addr) ++{ ++ unsigned long x; ++ get_dbe(x, (unsigned long *)addr);; ++} + +-#define BCM4710_FILL_TLB(addr) (*(volatile unsigned long *)(addr)) +-#define BCM4710_PROTECTED_FILL_TLB(addr) ({ unsigned long x; get_dbe(x, (volatile unsigned long *)(addr)); }) + #else + #define BCM4710_DUMMY_RREG() + +--- a/arch/mips/mm/tlbex.c ++++ b/arch/mips/mm/tlbex.c +@@ -914,6 +914,9 @@ build_get_pgde32(u32 **p, unsigned int t + #endif + uasm_i_addu(p, ptr, tmp, ptr); + #else ++#ifdef CONFIG_BCM47XX ++ uasm_i_nop(p); ++#endif + UASM_i_LA_mostly(p, ptr, pgdc); + #endif + uasm_i_mfc0(p, tmp, C0_BADVADDR); /* get faulting address */ +@@ -1264,12 +1267,12 @@ static void __cpuinit build_r4000_tlb_re + /* No need for uasm_i_nop */ + } + +-#ifdef CONFIG_BCM47XX +- uasm_i_nop(&p); +-#endif + #ifdef CONFIG_64BIT + build_get_pmde64(&p, &l, &r, K0, K1); /* get pmd in K1 */ + #else ++# ifdef CONFIG_BCM47XX ++ uasm_i_nop(&p); ++# endif + build_get_pgde32(&p, K0, K1); /* get pgd in K1 */ + #endif + +@@ -1281,6 +1284,9 @@ static void __cpuinit build_r4000_tlb_re + build_update_entries(&p, K0, K1); + build_tlb_write_entry(&p, &l, &r, tlb_random); + uasm_l_leave(&l, p); ++#ifdef CONFIG_BCM47XX ++ uasm_i_nop(&p); ++#endif + uasm_i_eret(&p); /* return from trap */ + } + #ifdef CONFIG_HUGETLB_PAGE +@@ -1797,12 +1803,12 @@ build_r4000_tlbchange_handler_head(u32 * + { + struct work_registers wr = build_get_work_registers(p); + +-#ifdef CONFIG_BCM47XX +- uasm_i_nop(p); +-#endif + #ifdef CONFIG_64BIT + build_get_pmde64(p, l, r, wr.r1, wr.r2); /* get pmd in ptr */ + #else ++# ifdef CONFIG_BCM47XX ++ uasm_i_nop(p); ++# endif + build_get_pgde32(p, wr.r1, wr.r2); /* get pgd in ptr */ + #endif + +@@ -1841,6 +1847,9 @@ build_r4000_tlbchange_handler_tail(u32 * + build_tlb_write_entry(p, l, r, tlb_indexed); + uasm_l_leave(l, *p); + build_restore_work_registers(p); ++#ifdef CONFIG_BCM47XX ++ uasm_i_nop(p); ++#endif + uasm_i_eret(p); /* return from trap */ + + #ifdef CONFIG_64BIT +--- a/arch/mips/kernel/genex.S ++++ b/arch/mips/kernel/genex.S +@@ -22,6 +22,19 @@ + #include <asm/page.h> + #include <asm/thread_info.h> + ++#ifdef CONFIG_BCM47XX ++# ifdef eret ++# undef eret ++# endif ++# define eret \ ++ .set push; \ ++ .set noreorder; \ ++ nop; \ ++ nop; \ ++ eret; \ ++ .set pop; ++#endif ++ + #define PANIC_PIC(msg) \ + .set push; \ + .set reorder; \ +@@ -54,7 +67,6 @@ NESTED(except_vec3_generic, 0, sp) + .set noat + #ifdef CONFIG_BCM47XX + nop +- nop + #endif + #if R5432_CP0_INTERRUPT_WAR + mfc0 k0, CP0_INDEX +@@ -79,6 +91,9 @@ NESTED(except_vec3_r4000, 0, sp) + .set push + .set mips3 + .set noat ++#ifdef CONFIG_BCM47XX ++ nop ++#endif + mfc0 k1, CP0_CAUSE + li k0, 31<<2 + andi k1, k1, 0x7c diff --git a/target/linux/brcm47xx/patches-3.3/940-bcm47xx-yenta.patch b/target/linux/brcm47xx/patches-3.3/940-bcm47xx-yenta.patch new file mode 100644 index 000000000..c65958eda --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/940-bcm47xx-yenta.patch @@ -0,0 +1,46 @@ +--- a/drivers/pcmcia/yenta_socket.c ++++ b/drivers/pcmcia/yenta_socket.c +@@ -920,6 +920,8 @@ static unsigned int yenta_probe_irq(stru + * Probe for usable interrupts using the force + * register to generate bogus card status events. + */ ++#ifndef CONFIG_BCM47XX ++ /* WRT54G3G does not like this */ + cb_writel(socket, CB_SOCKET_EVENT, -1); + cb_writel(socket, CB_SOCKET_MASK, CB_CSTSMASK); + reg = exca_readb(socket, I365_CSCINT); +@@ -935,6 +937,7 @@ static unsigned int yenta_probe_irq(stru + } + cb_writel(socket, CB_SOCKET_MASK, 0); + exca_writeb(socket, I365_CSCINT, reg); ++#endif + + mask = probe_irq_mask(val) & 0xffff; + +@@ -1019,6 +1022,10 @@ static void yenta_get_socket_capabilitie + else + socket->socket.irq_mask = 0; + ++ /* irq mask probing is broken for the WRT54G3G */ ++ if (socket->socket.irq_mask == 0) ++ socket->socket.irq_mask = 0x6f8; ++ + dev_printk(KERN_INFO, &socket->dev->dev, + "ISA IRQ mask 0x%04x, PCI irq %d\n", + socket->socket.irq_mask, socket->cb_irq); +@@ -1257,6 +1264,15 @@ static int __devinit yenta_probe(struct + dev_printk(KERN_INFO, &dev->dev, + "Socket status: %08x\n", cb_readl(socket, CB_SOCKET_STATE)); + ++ /* Generate an interrupt on card insert/remove */ ++ config_writew(socket, CB_SOCKET_MASK, CB_CSTSMASK | CB_CDMASK); ++ ++ /* Set up Multifunction Routing Status Register */ ++ config_writew(socket, 0x8C, 0x1000 /* MFUNC3 to GPIO3 */ | 0x2 /* MFUNC0 to INTA */); ++ ++ /* Switch interrupts to parallelized */ ++ config_writeb(socket, 0x92, 0x64); ++ + yenta_fixup_parent_bridge(dev->subordinate); + + /* Register it with the pcmcia layer.. */ diff --git a/target/linux/brcm47xx/patches-3.3/976-ssb_increase_pci_delay.patch b/target/linux/brcm47xx/patches-3.3/976-ssb_increase_pci_delay.patch new file mode 100644 index 000000000..eb70c81e6 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/976-ssb_increase_pci_delay.patch @@ -0,0 +1,11 @@ +--- a/drivers/ssb/driver_pcicore.c ++++ b/drivers/ssb/driver_pcicore.c +@@ -376,7 +376,7 @@ static void __devinit ssb_pcicore_init_h + set_io_port_base(ssb_pcicore_controller.io_map_base); + /* Give some time to the PCI controller to configure itself with the new + * values. Not waiting at this point causes crashes of the machine. */ +- mdelay(10); ++ mdelay(300); + register_pci_controller(&ssb_pcicore_controller); + } + diff --git a/target/linux/brcm47xx/patches-3.3/980-wnr834b_no_cardbus_invariant.patch b/target/linux/brcm47xx/patches-3.3/980-wnr834b_no_cardbus_invariant.patch new file mode 100644 index 000000000..7626c62ab --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/980-wnr834b_no_cardbus_invariant.patch @@ -0,0 +1,13 @@ +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -151,6 +151,10 @@ static int bcm47xx_get_invariants(struct + if (nvram_getenv("cardbus", buf, sizeof(buf)) >= 0) + iv->has_cardbus_slot = !!simple_strtoul(buf, NULL, 10); + ++ /* Do not indicate cardbus for Netgear WNR834B V1 and V2 */ ++ if (iv->boardinfo.type == 0x0472 && iv->has_cardbus_slot) ++ iv->has_cardbus_slot = 0; ++ + return 0; + } + diff --git a/target/linux/brcm47xx/patches-3.3/999-wl_exports.patch b/target/linux/brcm47xx/patches-3.3/999-wl_exports.patch new file mode 100644 index 000000000..e45b40242 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/999-wl_exports.patch @@ -0,0 +1,22 @@ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -24,7 +24,8 @@ + #include <asm/mach-bcm47xx/bus.h> + #include <linux/mtd/bcm47xx_nand.h> + +-static char nvram_buf[NVRAM_SPACE]; ++char nvram_buf[NVRAM_SPACE]; ++EXPORT_SYMBOL(nvram_buf); + static int cfe_env; + extern char *cfe_env_get(char *nv_buf, const char *name); + +--- a/arch/mips/mm/cache.c ++++ b/arch/mips/mm/cache.c +@@ -58,6 +58,7 @@ void (*_dma_cache_wback)(unsigned long s + void (*_dma_cache_inv)(unsigned long start, unsigned long size); + + EXPORT_SYMBOL(_dma_cache_wback_inv); ++EXPORT_SYMBOL(_dma_cache_inv); + + #endif /* CONFIG_DMA_NONCOHERENT */ + |