diff options
author | Hauke Mehrtens <hauke@hauke-m.de> | 2012-11-18 21:27:00 +0000 |
---|---|---|
committer | Hauke Mehrtens <hauke@hauke-m.de> | 2012-11-18 21:27:00 +0000 |
commit | 86cc79ac985ffd954d26c88d144b82063a714636 (patch) | |
tree | d750f1c9a309e6ff92cdd4233d6b9749ce2bc996 | |
parent | d648dad7fa7708a1879579ac6a6e87825eccd028 (diff) | |
download | upstream-86cc79ac985ffd954d26c88d144b82063a714636.tar.gz upstream-86cc79ac985ffd954d26c88d144b82063a714636.tar.bz2 upstream-86cc79ac985ffd954d26c88d144b82063a714636.zip |
brcm47xx: add support for kernel 3.6
This is based on the patch by Peter Wagner.
SVN-Revision: 34252
45 files changed, 7712 insertions, 0 deletions
diff --git a/package/broadcom-wl/patches/011-fix_compile_3_4.patch b/package/broadcom-wl/patches/011-fix_compile_3_4.patch new file mode 100644 index 0000000000..585d53c7df --- /dev/null +++ b/package/broadcom-wl/patches/011-fix_compile_3_4.patch @@ -0,0 +1,12 @@ +--- a/driver/wl_linux.c ++++ b/driver/wl_linux.c +@@ -49,7 +49,9 @@ + #include <linux/ieee80211.h> + #endif + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0) + #include <asm/system.h> ++#endif + #include <asm/io.h> + #include <asm/irq.h> + #include <asm/pgtable.h> diff --git a/target/linux/brcm47xx/config-3.6 b/target/linux/brcm47xx/config-3.6 new file mode 100644 index 0000000000..278e58cf0f --- /dev/null +++ b/target/linux/brcm47xx/config-3.6 @@ -0,0 +1,148 @@ +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_ARCH_DISCARD_MEMBLOCK=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +# CONFIG_ARPD is not set +CONFIG_B44=y +CONFIG_B44_PCI=y +CONFIG_B44_PCICORE_AUTOSELECT=y +CONFIG_B44_PCI_AUTOSELECT=y +CONFIG_BCM47XX=y +CONFIG_BCM47XX_BCMA=y +CONFIG_BCM47XX_SSB=y +CONFIG_BCM47XX_WDT=y +CONFIG_BCMA=y +CONFIG_BCMA_BLOCKIO=y +CONFIG_BCMA_DEBUG=y +CONFIG_BCMA_DRIVER_GMAC_CMN=y +CONFIG_BCMA_DRIVER_MIPS=y +CONFIG_BCMA_DRIVER_PCI_HOSTMODE=y +CONFIG_BCMA_HOST_PCI=y +CONFIG_BCMA_HOST_PCI_POSSIBLE=y +CONFIG_BCMA_HOST_SOC=y +CONFIG_BCMA_NFLASH=y +CONFIG_BCMA_POSSIBLE=y +CONFIG_BCMA_SFLASH=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_CEVT_R4K=y +CONFIG_CEVT_R4K_LIB=y +CONFIG_CFE=y +CONFIG_CMDLINE="root=/dev/mtdblock2 rootfstype=squashfs,jffs2 noinitrd console=ttyS0,115200" +CONFIG_CMDLINE_BOOL=y +# CONFIG_CMDLINE_OVERRIDE is not set +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPU_MIPS32=y +CONFIG_CPU_MIPS32_R1=y +# CONFIG_CPU_MIPS32_R2 is not set +CONFIG_CPU_MIPSR1=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CSRC_R4K=y +CONFIG_CSRC_R4K_LIB=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_IO=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y +CONFIG_HARDWARE_WATCHPOINTS=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_HAVE_ARCH_KGDB=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_HAVE_GENERIC_HARDIRQS=y +CONFIG_HAVE_IDE=y +CONFIG_HAVE_IRQ_WORK=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_HAVE_MEMBLOCK_NODE_MAP=y +CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_HW_HAS_PCI=y +CONFIG_HW_RANDOM=y +CONFIG_HZ=250 +# CONFIG_HZ_100 is not set +CONFIG_HZ_250=y +CONFIG_IMAGE_CMDLINE_HACK=y +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_IP_ROUTE_VERBOSE is not set +CONFIG_IRQ_CPU=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_MDIO_BOARDINFO=y +CONFIG_MIPS=y +CONFIG_MIPS_L1_CACHE_SHIFT=5 +# CONFIG_MIPS_MACHINE is not set +CONFIG_MIPS_MT_DISABLED=y +CONFIG_MTD_BCM47XX_PARTS=y +CONFIG_MTD_BCM47XX_PFLASH=y +CONFIG_MTD_BCM47XX_SFLASH=y +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_BCM47XX=y +CONFIG_MTD_NAND_ECC=y +# CONFIG_MTD_SM_COMMON is not set +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_PER_CPU_KM=y +CONFIG_NO_EXCEPT_FILL=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_PCI=y +CONFIG_PCI_DISABLE_COMMON_QUIRKS=y +CONFIG_PCI_DOMAINS=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PHYLIB=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +CONFIG_SERIAL_8250_EXTENDED=y +# CONFIG_SERIAL_8250_MANY_PORTS is not set +# CONFIG_SERIAL_8250_RSA is not set +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SSB=y +CONFIG_SSB_B43_PCI_BRIDGE=y +CONFIG_SSB_BLOCKIO=y +CONFIG_SSB_DEBUG=y +CONFIG_SSB_DRIVER_EXTIF=y +CONFIG_SSB_DRIVER_GIGE=y +CONFIG_SSB_DRIVER_MIPS=y +CONFIG_SSB_DRIVER_PCICORE=y +CONFIG_SSB_DRIVER_PCICORE_POSSIBLE=y +CONFIG_SSB_EMBEDDED=y +CONFIG_SSB_PCICORE_HOSTMODE=y +CONFIG_SSB_PCIHOST=y +CONFIG_SSB_PCIHOST_POSSIBLE=y +CONFIG_SSB_SERIAL=y +CONFIG_SSB_SFLASH=y +CONFIG_SSB_SPROM=y +CONFIG_SYS_HAS_CPU_MIPS32_R1=y +CONFIG_SYS_HAS_CPU_MIPS32_R2=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y +CONFIG_USB_ARCH_HAS_XHCI=y +# CONFIG_USB_HCD_BCMA is not set +# CONFIG_USB_HCD_SSB is not set +CONFIG_USB_SUPPORT=y +CONFIG_WATCHDOG_NOWAYOUT=y +CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/brcm47xx/patches-3.6/050-mtd-add-bcm47xx-part-parser.patch b/target/linux/brcm47xx/patches-3.6/050-mtd-add-bcm47xx-part-parser.patch new file mode 100644 index 0000000000..61e8838c0d --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/050-mtd-add-bcm47xx-part-parser.patch @@ -0,0 +1,570 @@ +--- 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,541 @@ ++/* ++ * 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); ++ 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.6/051-mtd-add-parallel-flash-driver.patch b/target/linux/brcm47xx/patches-3.6/051-mtd-add-parallel-flash-driver.patch new file mode 100644 index 0000000000..f28b6969a6 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/051-mtd-add-parallel-flash-driver.patch @@ -0,0 +1,224 @@ +--- a/drivers/mtd/maps/Kconfig ++++ b/drivers/mtd/maps/Kconfig +@@ -249,6 +249,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,196 @@ ++/* ++ * 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_pflash_probe(struct platform_device *pdev) ++{ ++#ifdef CONFIG_BCM47XX_SSB ++ struct ssb_mipscore *ssb_mcore; ++#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_mcore = &bcm47xx_bus.ssb.mipscore; ++ if (!ssb_mcore->pflash.present) ++ return -ENODEV; ++ ++ bcm47xx_map.phys = ssb_mcore->pflash.window; ++ bcm47xx_map.size = ssb_mcore->pflash.window_size; ++ bcm47xx_map.bankwidth = ssb_mcore->pflash.buswidth; ++ break; ++#endif ++#ifdef CONFIG_BCM47XX_BCMA ++ case BCM47XX_BUS_TYPE_BCMA: ++ bcma_cc = &bcm47xx_bus.bcma.bus.drv_cc; ++ if (!bcma_cc->pflash.present) ++ 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_pflash_remove(struct platform_device *pdev) ++{ ++ mtd_device_unregister(bcm47xx_mtd); ++ map_destroy(bcm47xx_mtd); ++ iounmap(bcm47xx_map.virt); ++ return 0; ++} ++ ++static const struct platform_device_id bcm47xx_pflash_table[] = { ++ { "bcm47xx-pflash", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(platform, bcm47xx_pflash_table); ++ ++static struct platform_driver bcm47xx_pflash_driver = { ++ .id_table = bcm47xx_pflash_table, ++ .probe = bcm47xx_pflash_probe, ++ .remove = __devexit_p(bcm47xx_pflash_remove), ++ .driver = { ++ .name = "bcm47xx-pflash", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init init_bcm47xx_pflash(void) ++{ ++ int ret = platform_driver_register(&bcm47xx_pflash_driver); ++ ++ if (ret) ++ pr_err("error registering platform driver: %i\n", ret); ++ return ret; ++} ++ ++static void __exit exit_bcm47xx_pflash(void) ++{ ++ platform_driver_unregister(&bcm47xx_pflash_driver); ++} ++ ++module_init(init_bcm47xx_pflash); ++module_exit(exit_bcm47xx_pflash); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("BCM47XX parallel flash driver"); diff --git a/target/linux/brcm47xx/patches-3.6/052-mtd-add-serial-flash-driver.patch b/target/linux/brcm47xx/patches-3.6/052-mtd-add-serial-flash-driver.patch new file mode 100644 index 0000000000..1d7b92739f --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/052-mtd-add-serial-flash-driver.patch @@ -0,0 +1,335 @@ +--- a/drivers/mtd/maps/Kconfig ++++ b/drivers/mtd/maps/Kconfig +@@ -258,6 +258,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,270 @@ ++/* ++ * 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 <linux/mtd/bcm47xx_sflash.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 const struct platform_device_id bcm47xx_sflash_table[] = { ++ { "bcm47xx-sflash", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(platform, bcm47xx_sflash_table); ++ ++static struct platform_driver bcm47xx_sflash_driver = { ++ .id_table = bcm47xx_sflash_table, ++ .probe = bcm47xx_sflash_probe, ++ .remove = __devexit_p(bcm47xx_sflash_remove), ++ .driver = { ++ .name = "bcm47xx-sflash", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init init_bcm47xx_sflash(void) ++{ ++ int ret = platform_driver_register(&bcm47xx_sflash_driver); ++ ++ 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 serial flash driver"); +--- /dev/null ++++ b/include/linux/mtd/bcm47xx_sflash.h +@@ -0,0 +1,34 @@ ++#ifndef LINUX_MTD_BCM47XX_SFLASH_H_ ++#define LINUX_MTD_BCM47XX_SFLASH_H_ ++ ++#include <linux/mtd/mtd.h> ++ ++enum bcm47xx_sflash_type { ++ BCM47XX_SFLASH_SSB, ++ BCM47XX_SFLASH_BCMA, ++}; ++ ++struct ssb_chipcommon; ++struct bcma_drv_cc; ++ ++struct bcm47xx_sflash { ++ enum bcm47xx_sflash_type type; ++ union { ++ struct ssb_chipcommon *scc; ++ struct bcma_drv_cc *bcc; ++ }; ++ ++ bool present; ++ u16 numblocks; ++ u32 window; ++ u32 blocksize; ++ u32 size; ++ ++ 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); ++ ++ struct mtd_info *mtd; ++}; ++#endif /* LINUX_MTD_BCM47XX_SFLASH_H_ */ diff --git a/target/linux/brcm47xx/patches-3.6/053-mtd-add-nand-flash-driver.patch b/target/linux/brcm47xx/patches-3.6/053-mtd-add-nand-flash-driver.patch new file mode 100644 index 0000000000..d37edfdffb --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/053-mtd-add-nand-flash-driver.patch @@ -0,0 +1,710 @@ +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -607,4 +607,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 +@@ -51,5 +51,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,528 @@ ++/* ++ * 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 <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_nflash_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip); ++static int bcm47xx_nflash_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_nflash_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_nflash_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_nflash_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_nflash_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_nflash_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_nflash_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_nflash_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_nflash_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_nflash_select_chip(struct mtd_info *mtd, int chip) ++{ ++} ++ ++static u_char bcm47xx_nflash_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_nflash_read_word(struct mtd_info *mtd) ++{ ++ loff_t from = read_offset; ++ uint16_t buf = 0; ++ int bytes; ++ ++ bcm47xx_nflash_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_nflash_write_buf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ bcm47xx_nflash_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_nflash_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ loff_t from = read_offset; ++ int bytes; ++ ++ bcm47xx_nflash_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_nflash_verify_buf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ return -EFAULT; ++} ++ ++static int bcm47xx_nflash_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_nflash_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 = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); ++ if (!mtd){ ++ ret = -ENOMEM; ++ goto err_out; ++ } ++ ++ nchip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); ++ if (!nchip) { ++ ret = -ENOMEM; ++ goto err_free_mtd; ++ } ++ ++ /* 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_nflash_dev_ready; ++ nchip->cmdfunc = bcm47xx_nflash_command; ++ nchip->select_chip = bcm47xx_nflash_select_chip; ++ nchip->read_byte = bcm47xx_nflash_read_byte; ++ nchip->read_word = bcm47xx_nflash_read_word; ++ nchip->write_buf = bcm47xx_nflash_write_buf; ++ nchip->read_buf = bcm47xx_nflash_read_buf; ++ nchip->verify_buf = bcm47xx_nflash_verify_buf; ++ nchip->block_bad = bcm47xx_nflash_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 err_free_nchip; ++ } ++ nflash->size = mtd->size; ++ nflash->pagesize = 1 << nchip->page_shift; ++ nflash->blocksize = mtd->erasesize; ++ nflash->mtd = mtd; ++ ++ /* second phase scan */ ++ if (nand_scan_tail(mtd)) { ++ pr_err("nand_scan_tail failed\n"); ++ ret = -ENXIO; ++ goto err_free_nchip; ++ } ++ ++ 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"); ++ goto err_free_nchip; ++ } ++ ++ return 0; ++ ++err_free_nchip: ++ kfree(nchip); ++err_free_mtd: ++ kfree(mtd); ++err_out: ++ return ret; ++} ++ ++static int __devexit bcm47xx_nflash_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); ++ kfree(mtd->priv); ++ kfree(mtd); ++ } ++ ++ return 0; ++} ++ ++static const struct platform_device_id bcm47xx_nflash_table[] = { ++ { "bcm47xx-nflash", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(platform, bcm47xx_nflash_table); ++ ++static struct platform_driver bcm47xx_nflash_driver = { ++ .id_table = bcm47xx_nflash_table, ++ .probe = bcm47xx_nflash_probe, ++ .remove = __devexit_p(bcm47xx_nflash_remove), ++ .driver = { ++ .name = "bcm47xx-nflash", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init init_bcm47xx_nflash(void) ++{ ++ int ret = platform_driver_register(&bcm47xx_nflash_driver); ++ ++ if (ret) ++ pr_err("error registering platform driver: %i\n", ret); ++ return ret; ++} ++ ++static void __exit exit_bcm47xx_nflash(void) ++{ ++ platform_driver_unregister(&bcm47xx_nflash_driver); ++} ++ ++module_init(init_bcm47xx_nflash); ++module_exit(exit_bcm47xx_nflash); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("BCM47XX NAND flash driver"); +--- /dev/null ++++ b/include/linux/mtd/bcm47xx_nand.h +@@ -0,0 +1,152 @@ ++/* ++ * 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 LINUX_MTD_BCM47XX_NAND_H_ ++#define LINUX_MTD_BCM47XX_NAND_H_ ++ ++#include <linux/mtd/mtd.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 ++ ++struct bcma_drv_cc; ++ ++struct bcm47xx_nflash { ++ struct bcma_drv_cc *bcc; ++ ++ bool present; ++ bool boot; /* This is the flash the SoC boots from */ ++ u32 blocksize; /* Block size */ ++ u32 pagesize; /* Page size */ ++ ++ u32 size; /* Total size in bytes */ ++ u32 next_opcode; /* Next expected command from upper NAND layer */ ++ ++ struct mtd_info *mtd; ++}; ++ ++#endif /* LINUX_MTD_BCM47XX_NAND_H_ */ diff --git a/target/linux/brcm47xx/patches-3.6/060-ssb-add-serial-flash-driver.patch b/target/linux/brcm47xx/patches-3.6/060-ssb-add-serial-flash-driver.patch new file mode 100644 index 0000000000..d65fc0a5b4 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/060-ssb-add-serial-flash-driver.patch @@ -0,0 +1,527 @@ +--- a/drivers/ssb/Kconfig ++++ b/drivers/ssb/Kconfig +@@ -143,6 +143,11 @@ config SSB_EMBEDDED + depends on SSB_DRIVER_MIPS + default y + ++config SSB_SFLASH ++ bool ++ depends on SSB_DRIVER_MIPS ++ default y ++ + config SSB_DRIVER_EXTIF + bool "SSB Broadcom EXTIF core driver" + depends on SSB_DRIVER_MIPS +--- 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,395 @@ ++/* ++ * Broadcom specific AMBA ++ * 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/platform_device.h> ++#include <linux/delay.h> ++#include <linux/ssb/ssb.h> ++#include <linux/ssb/ssb_driver_chipcommon.h> ++ ++#include "ssb_private.h" ++ ++#define NUM_RETRIES 3 ++ ++static struct resource ssb_sflash_resource = { ++ .name = "ssb_sflash", ++ .start = SSB_FLASH2, ++ .end = 0, ++ .flags = IORESOURCE_MEM | IORESOURCE_READONLY, ++}; ++ ++struct platform_device ssb_sflash_dev = { ++ .name = "bcm47xx-sflash", ++ .resource = &ssb_sflash_resource, ++ .num_resources = 1, ++}; ++ ++struct ssb_sflash_tbl_e { ++ char *name; ++ u32 id; ++ u32 blocksize; ++ u16 numblocks; ++}; ++ ++static const struct ssb_sflash_tbl_e ssb_sflash_st_tbl[] = { ++ { "M25P20", 0x11, 0x10000, 4, }, ++ { "M25P40", 0x12, 0x10000, 8, }, ++ ++ { "M25P16", 0x14, 0x10000, 32, }, ++ { "M25P32", 0x14, 0x10000, 64, }, ++ { "M25P64", 0x16, 0x10000, 128, }, ++ { "M25FL128", 0x17, 0x10000, 256, }, ++ { 0 }, ++}; ++ ++static const struct ssb_sflash_tbl_e ssb_sflash_sst_tbl[] = { ++ { "SST25WF512", 1, 0x1000, 16, }, ++ { "SST25VF512", 0x48, 0x1000, 16, }, ++ { "SST25WF010", 2, 0x1000, 32, }, ++ { "SST25VF010", 0x49, 0x1000, 32, }, ++ { "SST25WF020", 3, 0x1000, 64, }, ++ { "SST25VF020", 0x43, 0x1000, 64, }, ++ { "SST25WF040", 4, 0x1000, 128, }, ++ { "SST25VF040", 0x44, 0x1000, 128, }, ++ { "SST25VF040B", 0x8d, 0x1000, 128, }, ++ { "SST25WF080", 5, 0x1000, 256, }, ++ { "SST25VF080B", 0x8e, 0x1000, 256, }, ++ { "SST25VF016", 0x41, 0x1000, 512, }, ++ { "SST25VF032", 0x4a, 0x1000, 1024, }, ++ { "SST25VF064", 0x4b, 0x1000, 2048, }, ++ { 0 }, ++}; ++ ++static const struct ssb_sflash_tbl_e ssb_sflash_at_tbl[] = { ++ { "AT45DB011", 0xc, 256, 512, }, ++ { "AT45DB021", 0x14, 256, 1024, }, ++ { "AT45DB041", 0x1c, 256, 2048, }, ++ { "AT45DB081", 0x24, 256, 4096, }, ++ { "AT45DB161", 0x2c, 512, 4096, }, ++ { "AT45DB321", 0x34, 512, 8192, }, ++ { "AT45DB642", 0x3c, 1024, 8192, }, ++ { 0 }, ++}; ++ ++static void ssb_sflash_cmd(struct ssb_chipcommon *chipco, u32 opcode) ++{ ++ int i; ++ chipco_write32(chipco, SSB_CHIPCO_FLASHCTL, ++ SSB_CHIPCO_FLASHCTL_START | opcode); ++ for (i = 0; i < 1000; i++) { ++ if (!(chipco_read32(chipco, SSB_CHIPCO_FLASHCTL) & ++ SSB_CHIPCO_FLASHCTL_BUSY)) ++ return; ++ cpu_relax(); ++ } ++ pr_err("SFLASH control command failed (timeout)!\n"); ++} ++ ++static void ssb_sflash_write_u8(struct ssb_chipcommon *chipco, u32 offset, u8 byte) ++{ ++ chipco_write32(chipco, SSB_CHIPCO_FLASHADDR, offset); ++ chipco_write32(chipco, SSB_CHIPCO_FLASHDATA, byte); ++} ++ ++/* Read len bytes starting at offset into buf. Returns number of bytes read. */ ++static int ssb_sflash_read(struct bcm47xx_sflash *dev, u32 offset, u32 len, u8 *buf) ++{ ++ u8 *from, *to; ++ u32 cnt, i; ++ struct ssb_chipcommon *chipco = dev->scc; ++ ++ if (!len) ++ return 0; ++ ++ if ((offset + len) > chipco->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(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. */ ++static int ssb_sflash_poll(struct bcm47xx_sflash *dev, u32 offset) ++{ ++ struct ssb_chipcommon *chipco = dev->scc; ++ ++ if (offset >= chipco->sflash.size) ++ return -22; ++ ++ switch (chipco->capabilities & SSB_CHIPCO_CAP_FLASHT) { ++ case SSB_CHIPCO_FLASHT_STSER: ++ /* Check for ST Write In Progress bit */ ++ ssb_sflash_cmd(chipco, SSB_CHIPCO_FLASHCTL_ST_RDSR); ++ return chipco_read32(chipco, SSB_CHIPCO_FLASHDATA) ++ & SSB_CHIPCO_FLASHDATA_ST_WIP; ++ case SSB_CHIPCO_FLASHT_ATSER: ++ /* Check for Atmel Ready bit */ ++ ssb_sflash_cmd(chipco, SSB_CHIPCO_FLASHCTL_AT_STATUS); ++ return !(chipco_read32(chipco, SSB_CHIPCO_FLASHDATA) ++ & SSB_CHIPCO_FLASHDATA_AT_READY); ++ } ++ ++ return 0; ++} ++ ++ ++static int sflash_st_write(struct bcm47xx_sflash *dev, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ int written = 1; ++ struct ssb_chipcommon *chipco = dev->scc; ++ ++ /* Enable writes */ ++ ssb_sflash_cmd(chipco, SSB_CHIPCO_FLASHCTL_ST_WREN); ++ ssb_sflash_write_u8(chipco, offset, *buf++); ++ /* Issue a page program with CSA bit set */ ++ ssb_sflash_cmd(chipco, SSB_CHIPCO_FLASHCTL_ST_CSA | SSB_CHIPCO_FLASHCTL_ST_PP); ++ offset++; ++ len--; ++ while (len > 0) { ++ if ((offset & 255) == 0) { ++ /* Page boundary, poll droping cs and return */ ++ chipco_write32(chipco, SSB_CHIPCO_FLASHCTL, 0); ++ udelay(1); ++ if (!ssb_sflash_poll(dev, offset)) { ++ /* Flash rejected command */ ++ return -EAGAIN; ++ } ++ return written; ++ } else { ++ /* Write single byte */ ++ ssb_sflash_cmd(chipco, ++ SSB_CHIPCO_FLASHCTL_ST_CSA | ++ *buf++); ++ } ++ written++; ++ offset++; ++ len--; ++ } ++ /* All done, drop cs & poll */ ++ chipco_write32(chipco, SSB_CHIPCO_FLASHCTL, 0); ++ udelay(1); ++ if (!ssb_sflash_poll(dev, offset)) { ++ /* Flash rejected command */ ++ return -EAGAIN; ++ } ++ return written; ++} ++ ++static int sflash_at_write(struct bcm47xx_sflash *dev, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ struct ssb_chipcommon *chipco = dev->scc; ++ u32 page, byte, mask; ++ int ret = 0; ++ ++ mask = dev->blocksize - 1; ++ page = (offset & ~mask) << 1; ++ byte = offset & mask; ++ /* Read main memory page into buffer 1 */ ++ if (byte || (len < dev->blocksize)) { ++ int i = 100; ++ chipco_write32(chipco, SSB_CHIPCO_FLASHADDR, page); ++ ssb_sflash_cmd(chipco, SSB_CHIPCO_FLASHCTL_AT_BUF1_LOAD); ++ /* 250 us for AT45DB321B */ ++ while (i > 0 && ssb_sflash_poll(dev, offset)) { ++ udelay(10); ++ i--; ++ } ++ BUG_ON(!ssb_sflash_poll(dev, offset)); ++ } ++ /* Write into buffer 1 */ ++ for (ret = 0; (ret < (int)len) && (byte < dev->blocksize); ret++) { ++ ssb_sflash_write_u8(chipco, byte++, *buf++); ++ ssb_sflash_cmd(chipco, SSB_CHIPCO_FLASHCTL_AT_BUF1_WRITE); ++ } ++ /* Write buffer 1 into main memory page */ ++ chipco_write32(chipco, SSB_CHIPCO_FLASHADDR, page); ++ ssb_sflash_cmd(chipco, 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. ++ */ ++static int ssb_sflash_write(struct bcm47xx_sflash *dev, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ int ret = 0, tries = NUM_RETRIES; ++ struct ssb_chipcommon *chipco = dev->scc; ++ ++ if (!len) ++ return 0; ++ ++ if ((offset + len) > chipco->sflash.size) ++ return -EINVAL; ++ ++ switch (chipco->capabilities & SSB_CHIPCO_CAP_FLASHT) { ++ case SSB_CHIPCO_FLASHT_STSER: ++ do { ++ ret = sflash_st_write(dev, 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(dev, offset, len, buf); ++ break; ++ } ++ ++ return ret; ++} ++ ++/* Erase a region. Returns number of bytes scheduled for erasure. ++ * Caller should poll for completion. ++ */ ++static int ssb_sflash_erase(struct bcm47xx_sflash *dev, u32 offset) ++{ ++ struct ssb_chipcommon *chipco = dev->scc; ++ ++ if (offset >= chipco->sflash.size) ++ return -EINVAL; ++ ++ switch (chipco->capabilities & SSB_CHIPCO_CAP_FLASHT) { ++ case SSB_CHIPCO_FLASHT_STSER: ++ ssb_sflash_cmd(chipco, SSB_CHIPCO_FLASHCTL_ST_WREN); ++ chipco_write32(chipco, 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 (dev->blocksize < (64 * 1024)) ++ ssb_sflash_cmd(chipco, SSB_CHIPCO_FLASHCTL_ST_SSE); ++ else ++ ssb_sflash_cmd(chipco, SSB_CHIPCO_FLASHCTL_ST_SE); ++ return dev->blocksize; ++ case SSB_CHIPCO_FLASHT_ATSER: ++ chipco_write32(chipco, SSB_CHIPCO_FLASHADDR, offset << 1); ++ ssb_sflash_cmd(chipco, SSB_CHIPCO_FLASHCTL_AT_PAGE_ERASE); ++ return dev->blocksize; ++ } ++ ++ return 0; ++} ++ ++/* Initialize serial flash achipcoess */ ++int ssb_sflash_init(struct ssb_chipcommon *chipco) ++{ ++ struct bcm47xx_sflash *sflash = &chipco->sflash; ++ const struct ssb_sflash_tbl_e *e; ++ u32 id, id2; ++ ++ switch (chipco->capabilities & SSB_CHIPCO_CAP_FLASHT) { ++ case SSB_CHIPCO_FLASHT_STSER: ++ ssb_sflash_cmd(chipco, SSB_CHIPCO_FLASHCTL_ST_DP); ++ ++ chipco_write32(chipco, SSB_CHIPCO_FLASHADDR, 0); ++ ssb_sflash_cmd(chipco, SSB_CHIPCO_FLASHCTL_ST_RES); ++ id = chipco_read32(chipco, SSB_CHIPCO_FLASHDATA); ++ ++ chipco_write32(chipco, SSB_CHIPCO_FLASHADDR, 1); ++ ssb_sflash_cmd(chipco, SSB_CHIPCO_FLASHCTL_ST_RES); ++ id2 = chipco_read32(chipco, SSB_CHIPCO_FLASHDATA); ++ ++ switch (id) { ++ case 0xbf: ++ for (e = ssb_sflash_sst_tbl; e->name; e++) { ++ if (e->id == id2) ++ break; ++ } ++ break; ++ case 0x13: ++ return -ENOTSUPP; ++ default: ++ for (e = ssb_sflash_st_tbl; e->name; e++) { ++ if (e->id == id) ++ break; ++ } ++ break; ++ } ++ if (!e->name) { ++ pr_err("Unsupported ST serial flash (id: 0x%X, id2: 0x%X)\n", id, id2); ++ return -ENOTSUPP; ++ } ++ ++ break; ++ case SSB_CHIPCO_FLASHT_ATSER: ++ ssb_sflash_cmd(chipco, SSB_CHIPCO_FLASHCTL_AT_STATUS); ++ id = chipco_read32(chipco, SSB_CHIPCO_FLASHDATA) & 0x3c; ++ ++ for (e = ssb_sflash_at_tbl; e->name; e++) { ++ if (e->id == id) ++ break; ++ } ++ if (!e->name) { ++ pr_err("Unsupported Atmel serial flash (id: 0x%X)\n", id); ++ return -ENOTSUPP; ++ } ++ ++ break; ++ default: ++ pr_err("Unsupported flash type\n"); ++ return -ENOTSUPP; ++ } ++ ++ sflash->window = SSB_FLASH2; ++ sflash->blocksize = e->blocksize; ++ sflash->numblocks = e->numblocks; ++ sflash->size = sflash->blocksize * sflash->numblocks; ++ sflash->present = true; ++ sflash->read = ssb_sflash_read; ++ sflash->poll = ssb_sflash_poll; ++ sflash->write = ssb_sflash_write; ++ sflash->erase = ssb_sflash_erase; ++ sflash->type = BCM47XX_SFLASH_SSB; ++ sflash->scc = chipco; ++ ++ pr_info("Found %s serial flash (size: %dKiB, blocksize: 0x%X, blocks: %d)\n", ++ e->name, sflash->size / 1024, sflash->blocksize, ++ sflash->numblocks); ++ ++ /* Prepare platform device, but don't register it yet. It's too early, ++ * malloc (required by device_private_init) is not available yet. */ ++ ssb_sflash_dev.resource[0].end = ssb_sflash_dev.resource[0].start + ++ sflash->size; ++ ssb_sflash_dev.dev.platform_data = sflash; ++ ++ return 0; ++} +--- a/drivers/ssb/driver_mipscore.c ++++ b/drivers/ssb/driver_mipscore.c +@@ -203,7 +203,8 @@ 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: +- pr_err("Serial flash not supported\n"); ++ pr_debug("Found serial flash\n"); ++ ssb_sflash_init(&bus->chipco); + break; + case SSB_CHIPCO_FLASHT_PARA: + pr_debug("Found parallel flash\n"); +--- a/drivers/ssb/main.c ++++ b/drivers/ssb/main.c +@@ -18,6 +18,7 @@ + #include <linux/ssb/ssb_driver_gige.h> + #include <linux/dma-mapping.h> + #include <linux/pci.h> ++#include <linux/platform_device.h> + #include <linux/mmc/sdio_func.h> + #include <linux/slab.h> + +@@ -534,6 +535,15 @@ static int ssb_devices_register(struct s + dev_idx++; + } + ++#ifdef CONFIG_SSB_SFLASH ++ if (bus->chipco.sflash.present) { ++ err = platform_device_register(&ssb_sflash_dev); ++ if (err) ++ ssb_printk(KERN_ERR PFX ++ "Error registering serial flash\n"); ++ } ++#endif ++ + return 0; + error: + /* Unwind the already registered devices. */ +--- a/drivers/ssb/ssb_private.h ++++ b/drivers/ssb/ssb_private.h +@@ -211,4 +211,16 @@ static inline void b43_pci_ssb_bridge_ex + extern u32 ssb_pmu_get_cpu_clock(struct ssb_chipcommon *cc); + extern u32 ssb_pmu_get_controlclock(struct ssb_chipcommon *cc); + ++#ifdef CONFIG_SSB_SFLASH ++/* driver_chipcommon_sflash.c */ ++int ssb_sflash_init(struct ssb_chipcommon *chipco); ++extern struct platform_device ssb_sflash_dev; ++#else ++static inline int ssb_sflash_init(struct ssb_chipcommon *chipco) ++{ ++ pr_err("Serial flash not supported\n"); ++ return 0; ++} ++#endif /* CONFIG_SSB_SFLASH */ ++ + #endif /* LINUX_SSB_PRIVATE_H_ */ +--- a/include/linux/ssb/ssb_driver_chipcommon.h ++++ b/include/linux/ssb/ssb_driver_chipcommon.h +@@ -13,6 +13,8 @@ + * Licensed under the GPL version 2. See COPYING for details. + */ + ++#include <linux/mtd/bcm47xx_sflash.h> ++ + /** ChipCommon core registers. **/ + + #define SSB_CHIPCO_CHIPID 0x0000 +@@ -121,6 +123,17 @@ + #define SSB_CHIPCO_FLASHCTL_BUSY SSB_CHIPCO_FLASHCTL_START + #define SSB_CHIPCO_FLASHADDR 0x0044 + #define SSB_CHIPCO_FLASHDATA 0x0048 ++/* Status register bits for ST flashes */ ++#define SSB_CHIPCO_FLASHDATA_ST_WIP 0x01 /* Write In Progress */ ++#define SSB_CHIPCO_FLASHDATA_ST_WEL 0x02 /* Write Enable Latch */ ++#define SSB_CHIPCO_FLASHDATA_ST_BP_MASK 0x1c /* Block Protect */ ++#define SSB_CHIPCO_FLASHDATA_ST_BP_SHIFT 2 ++#define SSB_CHIPCO_FLASHDATA_ST_SRWD 0x80 /* Status Register Write Disable */ ++/* Status register bits for Atmel flashes */ ++#define SSB_CHIPCO_FLASHDATA_AT_READY 0x80 ++#define SSB_CHIPCO_FLASHDATA_AT_MISMATCH 0x40 ++#define SSB_CHIPCO_FLASHDATA_AT_ID_MASK 0x38 ++#define SSB_CHIPCO_FLASHDATA_AT_ID_SHIFT 3 + #define SSB_CHIPCO_BCAST_ADDR 0x0050 + #define SSB_CHIPCO_BCAST_DATA 0x0054 + #define SSB_CHIPCO_GPIOPULLUP 0x0058 /* Rev >= 20 only */ +@@ -503,7 +516,7 @@ + #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_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 */ +@@ -591,6 +604,9 @@ struct ssb_chipcommon { + /* Fast Powerup Delay constant */ + u16 fast_pwrup_delay; + struct ssb_chipcommon_pmu pmu; ++#ifdef CONFIG_SSB_SFLASH ++ struct bcm47xx_sflash sflash; ++#endif + }; + + static inline bool ssb_chipco_available(struct ssb_chipcommon *cc) diff --git a/target/linux/brcm47xx/patches-3.6/061-ssb-register-parallel-flash-device.patch b/target/linux/brcm47xx/patches-3.6/061-ssb-register-parallel-flash-device.patch new file mode 100644 index 0000000000..6e0d491842 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/061-ssb-register-parallel-flash-device.patch @@ -0,0 +1,76 @@ +--- a/drivers/ssb/driver_mipscore.c ++++ b/drivers/ssb/driver_mipscore.c +@@ -14,6 +14,7 @@ + #include <linux/serial_core.h> + #include <linux/serial_reg.h> + #include <linux/time.h> ++#include <linux/platform_device.h> + + #include "ssb_private.h" + +@@ -186,6 +187,19 @@ static void ssb_mips_serial_init(struct + mcore->nr_serial_ports = 0; + } + ++static struct resource ssb_pflash_resource = { ++ .name = "ssb_pflash", ++ .start = 0, ++ .end = 0, ++ .flags = 0, ++}; ++ ++struct platform_device ssb_pflash_dev = { ++ .name = "bcm47xx-pflash", ++ .resource = &ssb_pflash_resource, ++ .num_resources = 1, ++}; ++ + static void ssb_mips_flash_detect(struct ssb_mipscore *mcore) + { + struct ssb_bus *bus = mcore->dev->bus; +@@ -196,6 +210,9 @@ static void ssb_mips_flash_detect(struct + mcore->pflash.buswidth = 2; + mcore->pflash.window = SSB_FLASH1; + mcore->pflash.window_size = SSB_FLASH1_SZ; ++ ssb_pflash_resource.start = mcore->pflash.window; ++ ssb_pflash_resource.end = mcore->pflash.window + ++ mcore->pflash.window_size; + return; + } + +@@ -216,6 +233,9 @@ static void ssb_mips_flash_detect(struct + mcore->pflash.buswidth = 1; + else + mcore->pflash.buswidth = 2; ++ ssb_pflash_resource.start = mcore->pflash.window; ++ ssb_pflash_resource.end = mcore->pflash.window + ++ mcore->pflash.window_size; + break; + } + } +--- a/drivers/ssb/main.c ++++ b/drivers/ssb/main.c +@@ -543,6 +543,14 @@ static int ssb_devices_register(struct s + "Error registering serial flash\n"); + } + #endif ++#ifdef CONFIG_SSB_DRIVER_MIPS ++ if (bus->mipscore.pflash.present) { ++ err = platform_device_register(&ssb_pflash_dev); ++ if (err) ++ ssb_printk(KERN_ERR PFX ++ "Error registering parallel flash\n"); ++ } ++#endif + + return 0; + error: +--- a/drivers/ssb/ssb_private.h ++++ b/drivers/ssb/ssb_private.h +@@ -223,4 +223,6 @@ static inline int ssb_sflash_init(struct + } + #endif /* CONFIG_SSB_SFLASH */ + ++extern struct platform_device ssb_pflash_dev; ++ + #endif /* LINUX_SSB_PRIVATE_H_ */ diff --git a/target/linux/brcm47xx/patches-3.6/070-bcma-add-functions-to-write-to-serial-flash.patch b/target/linux/brcm47xx/patches-3.6/070-bcma-add-functions-to-write-to-serial-flash.patch new file mode 100644 index 0000000000..8fb70d68f4 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/070-bcma-add-functions-to-write-to-serial-flash.patch @@ -0,0 +1,345 @@ +--- a/drivers/bcma/driver_chipcommon_sflash.c ++++ b/drivers/bcma/driver_chipcommon_sflash.c +@@ -1,15 +1,22 @@ + /* + * Broadcom specific AMBA + * 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/platform_device.h> ++#include <linux/delay.h> + #include <linux/bcma/bcma.h> ++#include <linux/bcma/bcma_driver_chipcommon.h> + + #include "bcma_private.h" + ++#define NUM_RETRIES 3 ++ + static struct resource bcma_sflash_resource = { + .name = "bcma_sflash", + .start = BCMA_SOC_FLASH2, +@@ -18,7 +25,7 @@ static struct resource bcma_sflash_resou + }; + + struct platform_device bcma_sflash_dev = { +- .name = "bcma_sflash", ++ .name = "bcm47xx-sflash", + .resource = &bcma_sflash_resource, + .num_resources = 1, + }; +@@ -30,7 +37,7 @@ struct bcma_sflash_tbl_e { + u16 numblocks; + }; + +-static struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = { ++static const struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = { + { "M25P20", 0x11, 0x10000, 4, }, + { "M25P40", 0x12, 0x10000, 8, }, + +@@ -41,7 +48,7 @@ static struct bcma_sflash_tbl_e bcma_sfl + { 0 }, + }; + +-static struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = { ++static const struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = { + { "SST25WF512", 1, 0x1000, 16, }, + { "SST25VF512", 0x48, 0x1000, 16, }, + { "SST25WF010", 2, 0x1000, 32, }, +@@ -59,7 +66,7 @@ static struct bcma_sflash_tbl_e bcma_sfl + { 0 }, + }; + +-static struct bcma_sflash_tbl_e bcma_sflash_at_tbl[] = { ++static const struct bcma_sflash_tbl_e bcma_sflash_at_tbl[] = { + { "AT45DB011", 0xc, 256, 512, }, + { "AT45DB021", 0x14, 256, 1024, }, + { "AT45DB041", 0x1c, 256, 2048, }, +@@ -84,12 +91,230 @@ static void bcma_sflash_cmd(struct bcma_ + bcma_err(cc->core->bus, "SFLASH control command failed (timeout)!\n"); + } + ++static 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); ++} ++ ++/* Read len bytes starting at offset into buf. Returns number of bytes read. */ ++static int bcma_sflash_read(struct bcm47xx_sflash *dev, u32 offset, u32 len, u8 *buf) ++{ ++ u8 *from, *to; ++ u32 cnt, i; ++ struct bcma_drv_cc *cc = dev->bcc; ++ ++ 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_SOC_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. */ ++static int bcma_sflash_poll(struct bcm47xx_sflash *dev, u32 offset) ++{ ++ struct bcma_drv_cc *cc = dev->bcc; ++ ++ 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 bcm47xx_sflash *dev, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ int written = 1; ++ struct bcma_drv_cc *cc = dev->bcc; ++ ++ /* 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(dev, 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(dev, offset)) { ++ /* Flash rejected command */ ++ return -EAGAIN; ++ } ++ return written; ++} ++ ++static int sflash_at_write(struct bcm47xx_sflash *dev, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ struct bcma_drv_cc *cc = dev->bcc; ++ u32 page, byte, mask; ++ int ret = 0; ++ ++ mask = dev->blocksize - 1; ++ page = (offset & ~mask) << 1; ++ byte = offset & mask; ++ /* Read main memory page into buffer 1 */ ++ if (byte || (len < dev->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(dev, offset)) { ++ udelay(10); ++ i--; ++ } ++ BUG_ON(!bcma_sflash_poll(dev, offset)); ++ } ++ /* Write into buffer 1 */ ++ for (ret = 0; (ret < (int)len) && (byte < dev->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. ++ */ ++static int bcma_sflash_write(struct bcm47xx_sflash *dev, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ int ret = 0, tries = NUM_RETRIES; ++ struct bcma_drv_cc *cc = dev->bcc; ++ ++ if (!len) ++ return 0; ++ ++ if ((offset + len) > cc->sflash.size) ++ return -EINVAL; ++ ++ switch (cc->capabilities & BCMA_CC_CAP_FLASHT) { ++ case BCMA_CC_FLASHT_STSER: ++ do { ++ ret = sflash_st_write(dev, 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(dev, offset, len, buf); ++ break; ++ } ++ ++ return ret; ++} ++ ++/* Erase a region. Returns number of bytes scheduled for erasure. ++ * Caller should poll for completion. ++ */ ++static int bcma_sflash_erase(struct bcm47xx_sflash *dev, u32 offset) ++{ ++ struct bcma_drv_cc *cc = dev->bcc; ++ ++ if (offset >= cc->sflash.size) ++ return -EINVAL; ++ ++ 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 (dev->blocksize < (64 * 1024)) ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_SSE); ++ else ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_SE); ++ return dev->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 dev->blocksize; ++ } ++ ++ return 0; ++} ++ + /* Initialize serial flash access */ + int bcma_sflash_init(struct bcma_drv_cc *cc) + { + struct bcma_bus *bus = cc->core->bus; +- struct bcma_sflash *sflash = &cc->sflash; +- struct bcma_sflash_tbl_e *e; ++ struct bcm47xx_sflash *sflash = &cc->sflash; ++ const struct bcma_sflash_tbl_e *e; + u32 id, id2; + + switch (cc->capabilities & BCMA_CC_CAP_FLASHT) { +@@ -150,6 +375,12 @@ int bcma_sflash_init(struct bcma_drv_cc + sflash->numblocks = e->numblocks; + sflash->size = sflash->blocksize * sflash->numblocks; + sflash->present = true; ++ sflash->read = bcma_sflash_read; ++ sflash->poll = bcma_sflash_poll; ++ sflash->write = bcma_sflash_write; ++ sflash->erase = bcma_sflash_erase; ++ sflash->type = BCM47XX_SFLASH_BCMA; ++ sflash->bcc = cc; + + bcma_info(bus, "Found %s serial flash (size: %dKiB, blocksize: 0x%X, blocks: %d)\n", + e->name, sflash->size / 1024, sflash->blocksize, +--- a/include/linux/bcma/bcma_driver_chipcommon.h ++++ b/include/linux/bcma/bcma_driver_chipcommon.h +@@ -1,6 +1,8 @@ + #ifndef LINUX_BCMA_DRIVER_CC_H_ + #define LINUX_BCMA_DRIVER_CC_H_ + ++#include <linux/mtd/bcm47xx_sflash.h> ++ + /** ChipCommon core registers. **/ + #define BCMA_CC_ID 0x0000 + #define BCMA_CC_ID_ID 0x0000FFFF +@@ -516,17 +518,6 @@ struct bcma_pflash { + u32 window_size; + }; + +-#ifdef CONFIG_BCMA_SFLASH +-struct bcma_sflash { +- bool present; +- u32 window; +- u32 blocksize; +- u16 numblocks; +- u32 size; +- +- struct mtd_info *mtd; +-}; +-#endif + + #ifdef CONFIG_BCMA_NFLASH + struct mtd_info; +@@ -561,7 +552,7 @@ struct bcma_drv_cc { + #ifdef CONFIG_BCMA_DRIVER_MIPS + struct bcma_pflash pflash; + #ifdef CONFIG_BCMA_SFLASH +- struct bcma_sflash sflash; ++ struct bcm47xx_sflash sflash; + #endif + #ifdef CONFIG_BCMA_NFLASH + struct bcma_nflash nflash; diff --git a/target/linux/brcm47xx/patches-3.6/071-bcma-add-functions-to-write-to-nand-flash.patch b/target/linux/brcm47xx/patches-3.6/071-bcma-add-functions-to-write-to-nand-flash.patch new file mode 100644 index 0000000000..27c6677209 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/071-bcma-add-functions-to-write-to-nand-flash.patch @@ -0,0 +1,232 @@ +--- a/drivers/bcma/driver_chipcommon_nflash.c ++++ b/drivers/bcma/driver_chipcommon_nflash.c +@@ -2,16 +2,23 @@ + * Broadcom specific AMBA + * ChipCommon 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/delay.h> ++#include <linux/mtd/bcm47xx_nand.h> ++#include <linux/mtd/nand.h> + #include <linux/platform_device.h> + #include <linux/bcma/bcma.h> ++#include <linux/bcma/bcma_driver_chipcommon.h> + + #include "bcma_private.h" + + struct platform_device bcma_nflash_dev = { +- .name = "bcma_nflash", ++ .name = "bcm47xx-nflash", + .num_resources = 0, + }; + +@@ -31,6 +38,11 @@ int bcma_nflash_init(struct bcma_drv_cc + return -ENODEV; + } + ++ if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) { ++ bcma_err(bus, "NAND flash support for BCM4706 not implemented\n"); ++ return -ENOTSUPP; ++ } ++ + cc->nflash.present = true; + if (cc->core->id.rev == 38 && + (cc->status & BCMA_CC_CHIPST_5357_NAND_BOOT)) +@@ -42,3 +54,141 @@ int bcma_nflash_init(struct bcma_drv_cc + + return 0; + } ++ ++/* 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; ++} ++ ++#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; ++} ++ ++/* 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); ++} ++ ++/* 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/include/linux/bcma/bcma_driver_chipcommon.h ++++ b/include/linux/bcma/bcma_driver_chipcommon.h +@@ -2,6 +2,7 @@ + #define LINUX_BCMA_DRIVER_CC_H_ + + #include <linux/mtd/bcm47xx_sflash.h> ++#include <linux/mtd/bcm47xx_nand.h> + + /** ChipCommon core registers. **/ + #define BCMA_CC_ID 0x0000 +@@ -519,17 +520,6 @@ struct bcma_pflash { + }; + + +-#ifdef CONFIG_BCMA_NFLASH +-struct mtd_info; +- +-struct bcma_nflash { +- bool present; +- bool boot; /* This is the flash the SoC boots from */ +- +- struct mtd_info *mtd; +-}; +-#endif +- + struct bcma_serial_port { + void *regs; + unsigned long clockspeed; +@@ -555,7 +545,7 @@ struct bcma_drv_cc { + struct bcm47xx_sflash sflash; + #endif + #ifdef CONFIG_BCMA_NFLASH +- struct bcma_nflash nflash; ++ struct bcm47xx_nflash nflash; + #endif + + int nr_serial_ports; +@@ -613,4 +603,13 @@ 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_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_ */ diff --git a/target/linux/brcm47xx/patches-3.6/072-bcma-register-parallel-flash-device.patch b/target/linux/brcm47xx/patches-3.6/072-bcma-register-parallel-flash-device.patch new file mode 100644 index 0000000000..cacb903430 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/072-bcma-register-parallel-flash-device.patch @@ -0,0 +1,67 @@ +--- a/drivers/bcma/bcma_private.h ++++ b/drivers/bcma/bcma_private.h +@@ -45,6 +45,7 @@ int bcma_sprom_get(struct bcma_bus *bus) + /* driver_chipcommon.c */ + #ifdef CONFIG_BCMA_DRIVER_MIPS + void bcma_chipco_serial_init(struct bcma_drv_cc *cc); ++extern struct platform_device bcma_pflash_dev; + #endif /* CONFIG_BCMA_DRIVER_MIPS */ + + /* driver_chipcommon_pmu.c */ +--- a/drivers/bcma/driver_mips.c ++++ b/drivers/bcma/driver_mips.c +@@ -18,6 +18,7 @@ + #include <linux/serial_core.h> + #include <linux/serial_reg.h> + #include <linux/time.h> ++#include <linux/platform_device.h> + + /* The 47162a0 hangs when reading MIPS DMP registers registers */ + static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev) +@@ -178,6 +179,19 @@ u32 bcma_cpu_clock(struct bcma_drv_mips + } + EXPORT_SYMBOL(bcma_cpu_clock); + ++static struct resource bcma_pflash_resource = { ++ .name = "bcma_pflash", ++ .start = 0, ++ .end = 0, ++ .flags = 0, ++}; ++ ++struct platform_device bcma_pflash_dev = { ++ .name = "bcm47xx-pflash", ++ .resource = &bcma_pflash_resource, ++ .num_resources = 1, ++}; ++ + static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore) + { + struct bcma_bus *bus = mcore->core->bus; +@@ -200,6 +214,9 @@ static void bcma_core_mips_flash_detect( + cc->pflash.buswidth = 1; + else + cc->pflash.buswidth = 2; ++ ++ bcma_pflash_resource.start = cc->pflash.window; ++ bcma_pflash_resource.end = cc->pflash.window + cc->pflash.window_size; + break; + default: + bcma_err(bus, "Flash type not supported\n"); +--- a/drivers/bcma/main.c ++++ b/drivers/bcma/main.c +@@ -149,6 +149,14 @@ static int bcma_register_cores(struct bc + dev_id++; + } + ++#ifdef CONFIG_BCMA_DRIVER_MIPS ++ if (bus->drv_cc.pflash.present) { ++ err = platform_device_register(&bcma_pflash_dev); ++ if (err) ++ bcma_err(bus, "Error registering parallel flash\n"); ++ } ++#endif ++ + #ifdef CONFIG_BCMA_SFLASH + if (bus->drv_cc.sflash.present) { + err = platform_device_register(&bcma_sflash_dev); diff --git a/target/linux/brcm47xx/patches-3.6/080-MIPS-BCM47XX-rewrite-nvram-probing.patch b/target/linux/brcm47xx/patches-3.6/080-MIPS-BCM47XX-rewrite-nvram-probing.patch new file mode 100644 index 0000000000..a271876fa9 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/080-MIPS-BCM47XX-rewrite-nvram-probing.patch @@ -0,0 +1,183 @@ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -3,7 +3,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) 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 +@@ -23,69 +23,139 @@ + + static char nvram_buf[NVRAM_SPACE]; + ++static u32 find_nvram_size(u32 end) ++{ ++ struct nvram_header *header; ++ u32 nvram_sizes[] = {0x8000, 0xF000, 0x10000}; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) { ++ header = (struct nvram_header *)KSEG1ADDR(end - nvram_sizes[i]); ++ if (header->magic == NVRAM_HEADER) ++ return nvram_sizes[i]; ++ } ++ ++ return 0; ++} ++ + /* Probe for NVRAM header */ +-static void early_nvram_init(void) ++static void early_nvram_init_fill(u32 base, u32 lim) + { +-#ifdef CONFIG_BCM47XX_SSB +- struct ssb_mipscore *mcore_ssb; +-#endif +-#ifdef CONFIG_BCM47XX_BCMA +- struct bcma_drv_cc *bcma_cc; +-#endif + struct nvram_header *header; + int i; +- u32 base = 0; +- u32 lim = 0; + u32 off; + u32 *src, *dst; ++ u32 size; + +- switch (bcm47xx_bus_type) { +-#ifdef CONFIG_BCM47XX_SSB +- case BCM47XX_BUS_TYPE_SSB: +- mcore_ssb = &bcm47xx_bus.ssb.mipscore; +- base = mcore_ssb->pflash.window; +- lim = mcore_ssb->pflash.window_size; +- break; +-#endif +-#ifdef CONFIG_BCM47XX_BCMA +- case BCM47XX_BUS_TYPE_BCMA: +- bcma_cc = &bcm47xx_bus.bcma.bus.drv_cc; +- base = bcma_cc->pflash.window; +- lim = bcma_cc->pflash.window_size; +- break; +-#endif +- } +- ++ /* TODO: when nvram is on nand flash check for bad blocks first. */ + off = FLASH_MIN; + while (off <= lim) { + /* Windowed flash access */ +- header = (struct nvram_header *) +- KSEG1ADDR(base + off - NVRAM_SPACE); +- if (header->magic == NVRAM_HEADER) ++ size = find_nvram_size(base + off); ++ if (size) { ++ header = (struct nvram_header *)KSEG1ADDR(base + off - ++ size); + goto found; ++ } + off <<= 1; + } + + /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */ + header = (struct nvram_header *) KSEG1ADDR(base + 4096); +- if (header->magic == NVRAM_HEADER) ++ if (header->magic == NVRAM_HEADER) { ++ size = NVRAM_SPACE; + goto found; ++ } + + header = (struct nvram_header *) KSEG1ADDR(base + 1024); +- if (header->magic == NVRAM_HEADER) ++ if (header->magic == NVRAM_HEADER) { ++ size = NVRAM_SPACE; + goto found; ++ } + ++ pr_err("no nvram found\n"); + return; + + found: ++ ++ if (header->len > size) ++ pr_err("The nvram size accoridng to the header seems to be bigger than the partition on flash\n"); ++ if (header->len > NVRAM_SPACE) ++ pr_err("nvram on flash (%i bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n", ++ header->len, NVRAM_SPACE); ++ + src = (u32 *) header; + dst = (u32 *) nvram_buf; + for (i = 0; i < sizeof(struct nvram_header); i += 4) + *dst++ = *src++; +- for (; i < header->len && i < NVRAM_SPACE; i += 4) ++ for (; i < header->len && i < NVRAM_SPACE && i < size; i += 4) + *dst++ = le32_to_cpu(*src++); + } + ++#ifdef CONFIG_BCM47XX_BCMA ++static void early_nvram_init_bcma(void) ++{ ++ struct bcma_drv_cc *cc = &bcm47xx_bus.bcma.bus.drv_cc; ++ u32 base = 0; ++ u32 lim = 0; ++ ++ if (cc->nflash.boot) { ++ base = BCMA_SOC_FLASH1; ++ lim = BCMA_SOC_FLASH1_SZ; ++ } else if (cc->pflash.present) { ++ base = cc->pflash.window; ++ lim = cc->pflash.window_size; ++ } else if (cc->sflash.present) { ++ base = cc->sflash.window; ++ lim = cc->sflash.size; ++ } else { ++ pr_err("No supported flash found\n"); ++ return; ++ } ++ ++ early_nvram_init_fill(base, lim); ++} ++#endif ++ ++#ifdef CONFIG_BCM47XX_SSB ++static void early_nvram_init_ssb(void) ++{ ++ struct ssb_mipscore *mcore = &bcm47xx_bus.ssb.mipscore; ++ struct ssb_chipcommon *chipco = &bcm47xx_bus.ssb.chipco; ++ u32 base = 0; ++ u32 lim = 0; ++ ++ if (mcore->pflash.present) { ++ base = mcore->pflash.window; ++ lim = mcore->pflash.window_size; ++ } else if (chipco->sflash.present) { ++ base = chipco->sflash.window; ++ lim = chipco->sflash.size; ++ } else { ++ pr_err("No supported flash found\n"); ++ return; ++ } ++ ++ early_nvram_init_fill(base, lim); ++} ++#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) + { + char *var, *value, *end, *eq; diff --git a/target/linux/brcm47xx/patches-3.6/114-MIPS-BCM47xx-Setup-and-register-serial-early.patch b/target/linux/brcm47xx/patches-3.6/114-MIPS-BCM47xx-Setup-and-register-serial-early.patch new file mode 100644 index 0000000000..5e324e473b --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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 +@@ -31,6 +31,8 @@ + #include <linux/ssb/ssb.h> + #include <linux/ssb/ssb_embedded.h> + #include <linux/bcma/bcma_soc.h> ++#include <linux/serial.h> ++#include <linux/serial_8250.h> + #include <asm/bootinfo.h> + #include <asm/reboot.h> + #include <asm/time.h> +@@ -121,6 +123,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; +@@ -150,6 +177,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 + } + #endif + diff --git a/target/linux/brcm47xx/patches-3.6/116-MIPS-BCM47xx-Remove-CFE-console.patch b/target/linux/brcm47xx/patches-3.6/116-MIPS-BCM47xx-Remove-CFE-console.patch new file mode 100644 index 0000000000..8d2f7ca4e1 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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 +@@ -109,7 +109,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.6/119-fix-boot.patch b/target/linux/brcm47xx/patches-3.6/119-fix-boot.patch new file mode 100644 index 0000000000..6abfd8d27f --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/119-fix-boot.patch @@ -0,0 +1,16 @@ +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -101,11 +101,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.6/150-cpu_fixes.patch b/target/linux/brcm47xx/patches-3.6/150-cpu_fixes.patch new file mode 100644 index 0000000000..cbaef88ea3 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/150-cpu_fixes.patch @@ -0,0 +1,368 @@ +--- 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 +@@ -34,6 +34,9 @@ + #include <asm/cacheflush.h> /* for run_uncached() */ + #include <asm/traps.h> + ++/* For enabling BCM4710 cache workarounds */ ++int bcm4710 = 0; ++ + /* + * Special Variant of smp_call_function for use by cache functions: + * +@@ -110,6 +113,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) +@@ -126,6 +132,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) +@@ -142,6 +151,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) +@@ -682,6 +694,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: +@@ -1410,6 +1435,15 @@ void __cpuinit r4k_cache_init(void) + extern void build_copy_page(void); + struct cpuinfo_mips *c = ¤t_cpu_data; + ++ /* 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(); + +@@ -1470,6 +1504,14 @@ 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 + board_cache_error_setup = r4k_cache_error_setup; + } +--- a/arch/mips/mm/tlbex.c ++++ b/arch/mips/mm/tlbex.c +@@ -1267,6 +1267,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 +@@ -1797,6 +1800,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.6/160-kmap_coherent.patch b/target/linux/brcm47xx/patches-3.6/160-kmap_coherent.patch new file mode 100644 index 0000000000..4469714a47 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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 +@@ -506,7 +506,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); +@@ -529,7 +529,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); +--- 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); +- 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.6/170-fix-74k-cpu.patch b/target/linux/brcm47xx/patches-3.6/170-fix-74k-cpu.patch new file mode 100644 index 0000000000..a757ec7441 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/170-fix-74k-cpu.patch @@ -0,0 +1,12 @@ +--- a/arch/mips/kernel/cpu-probe.c ++++ b/arch/mips/kernel/cpu-probe.c +@@ -210,9 +210,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.6/186-USB-EHCI-bcma-fix-driver.patch b/target/linux/brcm47xx/patches-3.6/186-USB-EHCI-bcma-fix-driver.patch new file mode 100644 index 0000000000..b3800340c8 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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.6/191-MIPS-BCM47XX-ignore-last-memory-page.patch b/target/linux/brcm47xx/patches-3.6/191-MIPS-BCM47XX-ignore-last-memory-page.patch new file mode 100644 index 0000000000..9f7d5dcdb4 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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.6/192-MIPS-BCM47XX-improve-memory-size-detection.patch b/target/linux/brcm47xx/patches-3.6/192-MIPS-BCM47XX-improve-memory-size-detection.patch new file mode 100644 index 0000000000..285c60c713 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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.6/193-MIPS-BCM47xx-read-out-full-board-data.patch b/target/linux/brcm47xx/patches-3.6/193-MIPS-BCM47xx-read-out-full-board-data.patch new file mode 100644 index 0000000000..bdfc348cc7 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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.6/194-MIPS-BCM47XX-read-sprom-without-prefix-if-no-ieee802.patch b/target/linux/brcm47xx/patches-3.6/194-MIPS-BCM47XX-read-sprom-without-prefix-if-no-ieee802.patch new file mode 100644 index 0000000000..9487d301f2 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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 +@@ -206,6 +206,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.6/195-MIPS-BCM47xx-sprom-read-values-without-prefix-as-fal.patch b/target/linux/brcm47xx/patches-3.6/195-MIPS-BCM47xx-sprom-read-values-without-prefix-as-fal.patch new file mode 100644 index 0000000000..6b1db25805 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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 +@@ -96,7 +96,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"); +@@ -115,7 +115,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); +@@ -196,18 +196,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.6/210-b44_phy_fix.patch b/target/linux/brcm47xx/patches-3.6/210-b44_phy_fix.patch new file mode 100644 index 0000000000..a7929bb420 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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; +@@ -2104,6 +2129,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.6/211-b44_timeout_spam.patch b/target/linux/brcm47xx/patches-3.6/211-b44_timeout_spam.patch new file mode 100644 index 0000000000..c2eb3ad90b --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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.6/240-bcma-pcie-config-access.patch b/target/linux/brcm47xx/patches-3.6/240-bcma-pcie-config-access.patch new file mode 100644 index 0000000000..91b8a4191c --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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.6/280-activate_ssb_support_in_usb.patch b/target/linux/brcm47xx/patches-3.6/280-activate_ssb_support_in_usb.patch new file mode 100644 index 0000000000..8b858e11ba --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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_DRIVER_MIPS + bool "BCMA Broadcom MIPS core driver" +--- a/drivers/ssb/Kconfig ++++ b/drivers/ssb/Kconfig +@@ -146,6 +146,7 @@ config SSB_EMBEDDED + config SSB_SFLASH + 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.6/300-fork_cacheflush.patch b/target/linux/brcm47xx/patches-3.6/300-fork_cacheflush.patch new file mode 100644 index 0000000000..686fb1b945 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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.6/310-no_highpage.patch b/target/linux/brcm47xx/patches-3.6/310-no_highpage.patch new file mode 100644 index 0000000000..d88437ff76 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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); +- 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); +- copy_page(vto, vfrom); +- kunmap_atomic(vfrom); +- } +- 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); +- /* 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.6/400-arch-bcm47xx.patch b/target/linux/brcm47xx/patches-3.6/400-arch-bcm47xx.patch new file mode 100644 index 0000000000..bdd2c040dc --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/400-arch-bcm47xx.patch @@ -0,0 +1,33 @@ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -183,3 +183,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); diff --git a/target/linux/brcm47xx/patches-3.6/500-ssb-add-function-to-return-number-of-gpio-lines.patch b/target/linux/brcm47xx/patches-3.6/500-ssb-add-function-to-return-number-of-gpio-lines.patch new file mode 100644 index 0000000000..f1b483e4a4 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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.6/501-bcma-add-gpio-driver.patch b/target/linux/brcm47xx/patches-3.6/501-bcma-add-gpio-driver.patch new file mode 100644 index 0000000000..b8f72a12c6 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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 +@@ -551,6 +551,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 */ +@@ -581,13 +584,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.6/502-bcm47xx-rewrite-gpio-handling.patch b/target/linux/brcm47xx/patches-3.6/502-bcm47xx-rewrite-gpio-handling.patch new file mode 100644 index 0000000000..cff3184d88 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/502-bcm47xx-rewrite-gpio-handling.patch @@ -0,0 +1,493 @@ +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -101,6 +101,7 @@ config ATH79 + + config BCM47XX + bool "Broadcom BCM47XX based boards" ++ select ARCH_REQUIRE_GPIOLIB + select BOOT_RAW + select CEVT_R4K + select CSRC_R4K +@@ -110,7 +111,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 +@@ -252,6 +252,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", &bcm47xx_bus.ssb.chipco)) { +--- 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.6/610-pci_ide_fix.patch b/target/linux/brcm47xx/patches-3.6/610-pci_ide_fix.patch new file mode 100644 index 0000000000..f254b203bd --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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.6/700-ssb-gigabit-ethernet-driver.patch b/target/linux/brcm47xx/patches-3.6/700-ssb-gigabit-ethernet-driver.patch new file mode 100644 index 0000000000..211fc28e15 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/700-ssb-gigabit-ethernet-driver.patch @@ -0,0 +1,438 @@ +--- 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> + #if IS_ENABLED(CONFIG_HWMON) + #include <linux/hwmon.h> + #include <linux/hwmon-sysfs.h> +@@ -253,6 +254,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)}, +@@ -535,7 +537,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); + } + +@@ -545,7 +549,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); + } + +@@ -1052,7 +1057,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; +@@ -1068,7 +1074,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); +@@ -1105,7 +1111,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; +@@ -1123,7 +1135,7 @@ static int tg3_writephy(struct tg3 *tp, + + tg3_ape_lock(tp, tp->phy_ape_lock); + +- 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); +@@ -1158,6 +1170,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; +@@ -1730,6 +1747,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++) { +@@ -3312,6 +3334,8 @@ 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); +@@ -3387,6 +3411,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); +@@ -3401,9 +3430,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; + } + +@@ -3466,6 +3498,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 +@@ -3522,6 +3559,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)) +@@ -3862,8 +3904,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); +@@ -4365,6 +4408,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)) +@@ -4383,6 +4434,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; +@@ -8110,6 +8181,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) { +@@ -9729,6 +9808,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 +@@ -11424,6 +11508,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; + +@@ -12392,11 +12481,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; +@@ -12408,11 +12498,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; +@@ -13260,6 +13351,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 << +@@ -13752,10 +13850,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; +@@ -14765,6 +14872,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 +@@ -15174,6 +15286,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; +@@ -15458,7 +15574,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) +@@ -15820,6 +15937,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 +@@ -2973,6 +2973,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 +@@ -2121,6 +2121,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.6/812-disable_wgt634u_crap.patch b/target/linux/brcm47xx/patches-3.6/812-disable_wgt634u_crap.patch new file mode 100644 index 0000000000..2819ca1739 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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 +-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_mipscore *mcore = &bcm47xx_bus.ssb.mipscore; +- +- 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", &bcm47xx_bus.ssb.chipco)) { +- gpio_direction_input(WGT634U_GPIO_RESET); +- gpio_intmask(WGT634U_GPIO_RESET, 1); +- ssb_chipco_irq_mask(&bcm47xx_bus.ssb.chipco, +- SSB_CHIPCO_IRQ_GPIO, +- SSB_CHIPCO_IRQ_GPIO); +- } +- +- wgt634u_flash_data.width = mcore->pflash.buswidth; +- wgt634u_flash_resource.start = mcore->pflash.window; +- wgt634u_flash_resource.end = mcore->pflash.window +- + mcore->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.6/820-wgt634u-nvram-fix.patch b/target/linux/brcm47xx/patches-3.6/820-wgt634u-nvram-fix.patch new file mode 100644 index 0000000000..c2da5e0770 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/820-wgt634u-nvram-fix.patch @@ -0,0 +1,306 @@ +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 ++obj-y += gpio.o irq.o nvram.o prom.o serial.o setup.o time.o sprom.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 +@@ -22,6 +22,8 @@ + #include <asm/mach-bcm47xx/bcm47xx.h> + + static char nvram_buf[NVRAM_SPACE]; ++static int cfe_env; ++extern char *cfe_env_get(char *nv_buf, const char *name); + + static u32 find_nvram_size(u32 end) + { +@@ -47,6 +49,26 @@ static void early_nvram_init_fill(u32 ba + u32 *src, *dst; + u32 size; + ++ 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; ++ } ++ } ++ + /* TODO: when nvram is on nand flash check for bad blocks first. */ + off = FLASH_MIN; + while (off <= lim) { +@@ -166,6 +188,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; +@@ -194,6 +222,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.6/900-bcm47xx_wdt-noprescale.patch b/target/linux/brcm47xx/patches-3.6/900-bcm47xx_wdt-noprescale.patch new file mode 100644 index 0000000000..bf0a192684 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/900-bcm47xx_wdt-noprescale.patch @@ -0,0 +1,103 @@ +--- a/drivers/watchdog/bcm47xx_wdt.c ++++ b/drivers/watchdog/bcm47xx_wdt.c +@@ -33,6 +33,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 bool nowayout = WATCHDOG_NOWAYOUT; +@@ -52,20 +53,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 + } +@@ -90,33 +91,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 { + pr_crit("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) +@@ -267,7 +269,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.6/920-cache-wround.patch b/target/linux/brcm47xx/patches-3.6/920-cache-wround.patch new file mode 100644 index 0000000000..7cdcab048b --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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 +@@ -917,6 +917,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 */ +@@ -1267,12 +1270,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 + +@@ -1284,6 +1287,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 +@@ -1800,12 +1806,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 + +@@ -1844,6 +1850,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.6/940-bcm47xx-yenta.patch b/target/linux/brcm47xx/patches-3.6/940-bcm47xx-yenta.patch new file mode 100644 index 0000000000..c65958eda8 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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.6/976-ssb_increase_pci_delay.patch b/target/linux/brcm47xx/patches-3.6/976-ssb_increase_pci_delay.patch new file mode 100644 index 0000000000..eb70c81e65 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/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.6/980-wnr834b_no_cardbus_invariant.patch b/target/linux/brcm47xx/patches-3.6/980-wnr834b_no_cardbus_invariant.patch new file mode 100644 index 0000000000..1c0c6195e8 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/980-wnr834b_no_cardbus_invariant.patch @@ -0,0 +1,13 @@ +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -120,6 +120,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.6/999-wl_exports.patch b/target/linux/brcm47xx/patches-3.6/999-wl_exports.patch new file mode 100644 index 0000000000..1bf09f36b0 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.6/999-wl_exports.patch @@ -0,0 +1,22 @@ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -21,7 +21,8 @@ + #include <asm/mach-bcm47xx/nvram.h> + #include <asm/mach-bcm47xx/bcm47xx.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 */ + diff --git a/target/linux/generic/patches-3.6/025-bcma_backport.patch b/target/linux/generic/patches-3.6/025-bcma_backport.patch index 15f54a3ba7..d549bb42cb 100644 --- a/target/linux/generic/patches-3.6/025-bcma_backport.patch +++ b/target/linux/generic/patches-3.6/025-bcma_backport.patch @@ -1,3 +1,33 @@ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -43,8 +43,8 @@ static void early_nvram_init(void) + #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; ++ base = mcore_ssb->pflash.window; ++ lim = mcore_ssb->pflash.window_size; + break; + #endif + #ifdef CONFIG_BCM47XX_BCMA +--- a/arch/mips/bcm47xx/wgt634u.c ++++ b/arch/mips/bcm47xx/wgt634u.c +@@ -156,10 +156,10 @@ static int __init wgt634u_init(void) + 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 = mcore->pflash.buswidth; ++ wgt634u_flash_resource.start = mcore->pflash.window; ++ wgt634u_flash_resource.end = mcore->pflash.window ++ + mcore->pflash.window_size + - 1; + return platform_add_devices(wgt634u_devices, + ARRAY_SIZE(wgt634u_devices)); --- a/drivers/bcma/Kconfig +++ b/drivers/bcma/Kconfig @@ -48,12 +48,12 @@ config BCMA_DRIVER_MIPS @@ -1022,3 +1052,45 @@ +#define BCMA_SOC_FLASH2_SZ 0x02000000 /* Size of Flash Region 2 */ + #endif /* LINUX_BCMA_REGS_H_ */ +--- a/drivers/net/wireless/b43/main.c ++++ b/drivers/net/wireless/b43/main.c +@@ -4622,7 +4622,7 @@ static int b43_wireless_core_init(struct + switch (dev->dev->bus_type) { + #ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: +- bcma_core_pci_irq_ctl(&dev->dev->bdev->bus->drv_pci, ++ bcma_core_pci_irq_ctl(&dev->dev->bdev->bus->drv_pci[0], + dev->dev->bdev, true); + break; + #endif +--- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c ++++ b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c +@@ -695,7 +695,7 @@ void ai_pci_up(struct si_pub *sih) + sii = container_of(sih, struct si_info, pub); + + if (sii->icbus->hosttype == BCMA_HOSTTYPE_PCI) +- bcma_core_pci_extend_L1timer(&sii->icbus->drv_pci, true); ++ bcma_core_pci_extend_L1timer(&sii->icbus->drv_pci[0], true); + } + + /* Unconfigure and/or apply various WARs when going down */ +@@ -706,7 +706,7 @@ void ai_pci_down(struct si_pub *sih) + sii = container_of(sih, struct si_info, pub); + + if (sii->icbus->hosttype == BCMA_HOSTTYPE_PCI) +- bcma_core_pci_extend_L1timer(&sii->icbus->drv_pci, false); ++ bcma_core_pci_extend_L1timer(&sii->icbus->drv_pci[0], false); + } + + /* Enable BT-COEX & Ex-PA for 4313 */ +--- a/drivers/net/wireless/brcm80211/brcmsmac/main.c ++++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c +@@ -5077,7 +5077,7 @@ static int brcms_b_up_prep(struct brcms_ + * Configure pci/pcmcia here instead of in brcms_c_attach() + * to allow mfg hotswap: down, hotswap (chip power cycle), up. + */ +- bcma_core_pci_irq_ctl(&wlc_hw->d11core->bus->drv_pci, wlc_hw->d11core, ++ bcma_core_pci_irq_ctl(&wlc_hw->d11core->bus->drv_pci[0], wlc_hw->d11core, + true); + + /* |