From: Hauke Mehrtens Date: Thu, 3 May 2012 20:18:41 +0000 (+0000) Subject: brcm47xx: add support for kernel 3.3 X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=22b2e3fbb9cc78d671e44f788a062031c97ffc6f;p=openwrt%2Fstaging%2Fflorian.git brcm47xx: add support for kernel 3.3 SVN-Revision: 31573 --- diff --git a/target/linux/brcm47xx/config-3.3 b/target/linux/brcm47xx/config-3.3 new file mode 100644 index 0000000000..5f32b35d9e --- /dev/null +++ b/target/linux/brcm47xx/config-3.3 @@ -0,0 +1,150 @@ +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_ARCH_DISCARD_MEMBLOCK=y +# CONFIG_ARCH_DMA_ADDR_T_64BIT is not set +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=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_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_CPU_DEVICES is not set +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_PCI_IOMAP=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_KALLSYMS=y +CONFIG_LEDS_GPIO=y +CONFIG_MDIO_BOARDINFO=y +# CONFIG_MINIX_FS_NATIVE_ENDIAN is not set +CONFIG_MIPS=y +CONFIG_MIPS_L1_CACHE_SHIFT=5 +# CONFIG_MIPS_MACHINE is not set +CONFIG_MIPS_MT_DISABLED=y +# CONFIG_MLX4_CORE is not set +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_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_QUOTACTL 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_XZ_DEC=y +CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/brcm47xx/patches-3.3/020-bcma-move-parallel-flash-into-a-union.patch b/target/linux/brcm47xx/patches-3.3/020-bcma-move-parallel-flash-into-a-union.patch new file mode 100644 index 0000000000..0042ff7ed8 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/020-bcma-move-parallel-flash-into-a-union.patch @@ -0,0 +1,129 @@ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -50,6 +50,9 @@ static void early_nvram_init(void) + #ifdef CONFIG_BCM47XX_BCMA + case BCM47XX_BUS_TYPE_BCMA: + bcma_cc = &bcm47xx_bus.bcma.bus.drv_cc; ++ if (bcma_cc->flash_type != BCMA_PFLASH) ++ return; ++ + base = bcma_cc->pflash.window; + lim = bcma_cc->pflash.window_size; + break; +--- a/drivers/bcma/driver_mips.c ++++ b/drivers/bcma/driver_mips.c +@@ -189,6 +189,7 @@ static void bcma_core_mips_flash_detect( + break; + case BCMA_CC_FLASHT_PARA: + pr_info("found parallel flash.\n"); ++ bus->drv_cc.flash_type = BCMA_PFLASH; + bus->drv_cc.pflash.window = 0x1c000000; + bus->drv_cc.pflash.window_size = 0x02000000; + +--- a/include/linux/bcma/bcma_driver_chipcommon.h ++++ b/include/linux/bcma/bcma_driver_chipcommon.h +@@ -117,10 +117,68 @@ + #define BCMA_CC_JCTL_EXT_EN 2 /* Enable external targets */ + #define BCMA_CC_JCTL_EN 1 /* Enable Jtag master */ + #define BCMA_CC_FLASHCTL 0x0040 ++ ++/* Start/busy bit in flashcontrol */ ++#define BCMA_CC_FLASHCTL_OPCODE 0x000000ff ++#define BCMA_CC_FLASHCTL_ACTION 0x00000700 ++#define BCMA_CC_FLASHCTL_CS_ACTIVE 0x00001000 /* Chip Select Active, rev >= 20 */ + #define BCMA_CC_FLASHCTL_START 0x80000000 + #define BCMA_CC_FLASHCTL_BUSY BCMA_CC_FLASHCTL_START ++ ++/* flashcontrol action+opcodes for ST flashes */ ++#define BCMA_CC_FLASHCTL_ST_WREN 0x0006 /* Write Enable */ ++#define BCMA_CC_FLASHCTL_ST_WRDIS 0x0004 /* Write Disable */ ++#define BCMA_CC_FLASHCTL_ST_RDSR 0x0105 /* Read Status Register */ ++#define BCMA_CC_FLASHCTL_ST_WRSR 0x0101 /* Write Status Register */ ++#define BCMA_CC_FLASHCTL_ST_READ 0x0303 /* Read Data Bytes */ ++#define BCMA_CC_FLASHCTL_ST_PP 0x0302 /* Page Program */ ++#define BCMA_CC_FLASHCTL_ST_SE 0x02d8 /* Sector Erase */ ++#define BCMA_CC_FLASHCTL_ST_BE 0x00c7 /* Bulk Erase */ ++#define BCMA_CC_FLASHCTL_ST_DP 0x00b9 /* Deep Power-down */ ++#define BCMA_CC_FLASHCTL_ST_RES 0x03ab /* Read Electronic Signature */ ++#define BCMA_CC_FLASHCTL_ST_CSA 0x1000 /* Keep chip select asserted */ ++#define BCMA_CC_FLASHCTL_ST_SSE 0x0220 /* Sub-sector Erase */ ++ ++ ++/* flashcontrol action+opcodes for Atmel flashes */ ++#define BCMA_CC_FLASHCTL_AT_READ 0x07e8 ++#define BCMA_CC_FLASHCTL_AT_PAGE_READ 0x07d2 ++#define BCMA_CC_FLASHCTL_AT_BUF1_READ ++#define BCMA_CC_FLASHCTL_AT_BUF2_READ ++#define BCMA_CC_FLASHCTL_AT_STATUS 0x01d7 ++#define BCMA_CC_FLASHCTL_AT_BUF1_WRITE 0x0384 ++#define BCMA_CC_FLASHCTL_AT_BUF2_WRITE 0x0387 ++#define BCMA_CC_FLASHCTL_AT_BUF1_ERASE_PROGRAM 0x0283 ++#define BCMA_CC_FLASHCTL_AT_BUF2_ERASE_PROGRAM 0x0286 ++#define BCMA_CC_FLASHCTL_AT_BUF1_PROGRAM 0x0288 ++#define BCMA_CC_FLASHCTL_AT_BUF2_PROGRAM 0x0289 ++#define BCMA_CC_FLASHCTL_AT_PAGE_ERASE 0x0281 ++#define BCMA_CC_FLASHCTL_AT_BLOCK_ERASE 0x0250 ++#define BCMA_CC_FLASHCTL_AT_BUF1_WRITE_ERASE_PROGRAM 0x0382 ++#define BCMA_CC_FLASHCTL_AT_BUF2_WRITE_ERASE_PROGRAM 0x0385 ++#define BCMA_CC_FLASHCTL_AT_BUF1_LOAD 0x0253 ++#define BCMA_CC_FLASHCTL_AT_BUF2_LOAD 0x0255 ++#define BCMA_CC_FLASHCTL_AT_BUF1_COMPARE 0x0260 ++#define BCMA_CC_FLASHCTL_AT_BUF2_COMPARE 0x0261 ++#define BCMA_CC_FLASHCTL_AT_BUF1_REPROGRAM 0x0258 ++#define BCMA_CC_FLASHCTL_AT_BUF2_REPROGRAM 0x0259 ++ + #define BCMA_CC_FLASHADDR 0x0044 + #define BCMA_CC_FLASHDATA 0x0048 ++ ++/* Status register bits for ST flashes */ ++#define BCMA_CC_FLASHDATA_ST_WIP 0x01 /* Write In Progress */ ++#define BCMA_CC_FLASHDATA_ST_WEL 0x02 /* Write Enable Latch */ ++#define BCMA_CC_FLASHDATA_ST_BP_MASK 0x1c /* Block Protect */ ++#define BCMA_CC_FLASHDATA_ST_BP_SHIFT 2 ++#define BCMA_CC_FLASHDATA_ST_SRWD 0x80 /* Status Register Write Disable */ ++ ++/* Status register bits for Atmel flashes */ ++#define BCMA_CC_FLASHDATA_AT_READY 0x80 ++#define BCMA_CC_FLASHDATA_AT_MISMATCH 0x40 ++#define BCMA_CC_FLASHDATA_AT_ID_MASK 0x38 ++#define BCMA_CC_FLASHDATA_AT_ID_SHIFT 3 ++ + #define BCMA_CC_BCAST_ADDR 0x0050 + #define BCMA_CC_BCAST_DATA 0x0054 + #define BCMA_CC_GPIOPULLUP 0x0058 /* Rev >= 20 only */ +@@ -324,6 +382,12 @@ + #define BCMA_CHIPCTL_4331_BT_SHD0_ON_GPIO4 BIT(16) /* enable bt_shd0 at gpio4 */ + #define BCMA_CHIPCTL_4331_BT_SHD1_ON_GPIO5 BIT(17) /* enable bt_shd1 at gpio5 */ + ++#define BCMA_FLASH2 0x1c000000 /* Flash Region 2 (region 1 shadowed here) */ ++#define BCMA_FLASH2_SZ 0x02000000 /* Size of Flash Region 2 */ ++#define BCMA_FLASH1 0x1fc00000 /* MIPS Flash Region 1 */ ++#define BCMA_FLASH1_SZ 0x00400000 /* MIPS Size of Flash Region 1 */ ++ ++ + /* Data for the PMU, if available. + * Check availability with ((struct bcma_chipcommon)->capabilities & BCMA_CC_CAP_PMU) + */ +@@ -333,6 +397,10 @@ struct bcma_chipcommon_pmu { + }; + + #ifdef CONFIG_BCMA_DRIVER_MIPS ++enum bcma_flash_type { ++ BCMA_PFLASH, ++}; ++ + struct bcma_pflash { + u8 buswidth; + u32 window; +@@ -358,7 +426,10 @@ struct bcma_drv_cc { + u16 fast_pwrup_delay; + struct bcma_chipcommon_pmu pmu; + #ifdef CONFIG_BCMA_DRIVER_MIPS +- struct bcma_pflash pflash; ++ enum bcma_flash_type flash_type; ++ union { ++ struct bcma_pflash pflash; ++ }; + + int nr_serial_ports; + struct bcma_serial_port serial_ports[4]; diff --git a/target/linux/brcm47xx/patches-3.3/021-bcma-add-serial-flash-support-to-bcma.patch b/target/linux/brcm47xx/patches-3.3/021-bcma-add-serial-flash-support-to-bcma.patch new file mode 100644 index 0000000000..2de4ba0f52 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/021-bcma-add-serial-flash-support-to-bcma.patch @@ -0,0 +1,505 @@ +--- a/drivers/bcma/Kconfig ++++ b/drivers/bcma/Kconfig +@@ -38,6 +38,11 @@ config BCMA_HOST_SOC + bool + depends on BCMA_DRIVER_MIPS + ++config BCMA_SFLASH ++ bool ++ depends on BCMA_DRIVER_MIPS ++ default y ++ + config BCMA_DRIVER_MIPS + bool "BCMA Broadcom MIPS core driver" + depends on BCMA && MIPS +--- a/drivers/bcma/Makefile ++++ b/drivers/bcma/Makefile +@@ -1,5 +1,6 @@ + bcma-y += main.o scan.o core.o sprom.o + bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o ++bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o + bcma-y += driver_pci.o + bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o + bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o +--- a/drivers/bcma/bcma_private.h ++++ b/drivers/bcma/bcma_private.h +@@ -42,6 +42,11 @@ void bcma_chipco_serial_init(struct bcma + u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc); + u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc); + ++#ifdef CONFIG_BCMA_SFLASH ++/* driver_chipcommon_sflash.c */ ++int bcma_sflash_init(struct bcma_drv_cc *cc); ++#endif /* CONFIG_BCMA_SFLASH */ ++ + #ifdef CONFIG_BCMA_HOST_PCI + /* host_pci.c */ + extern int __init bcma_host_pci_init(void); +--- /dev/null ++++ b/drivers/bcma/driver_chipcommon_sflash.c +@@ -0,0 +1,398 @@ ++/* ++ * Broadcom SiliconBackplane chipcommon serial flash interface ++ * ++ * Copyright 2011, Jonas Gorski ++ * Copyright 2011, 2012, Hauke Mehrtens ++ * Copyright 2010, Broadcom Corporation ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++ ++#include ++#include ++#include ++ ++#include "bcma_private.h" ++ ++#define NUM_RETRIES 3 ++ ++ ++/* Issue a serial flash command */ ++static inline void bcma_sflash_cmd(struct bcma_drv_cc *cc, u32 opcode) ++{ ++ bcma_cc_write32(cc, BCMA_CC_FLASHCTL, ++ BCMA_CC_FLASHCTL_START | opcode); ++ while (bcma_cc_read32(cc, BCMA_CC_FLASHCTL) & BCMA_CC_FLASHCTL_BUSY) ++ ; ++} ++ ++ ++static inline void bcma_sflash_write_u8(struct bcma_drv_cc *cc, ++ u32 offset, u8 byte) ++{ ++ bcma_cc_write32(cc, BCMA_CC_FLASHADDR, offset); ++ bcma_cc_write32(cc, BCMA_CC_FLASHDATA, byte); ++} ++ ++/* Initialize serial flash access */ ++int bcma_sflash_init(struct bcma_drv_cc *cc) ++{ ++ u32 id, id2; ++ ++ memset(&cc->sflash, 0, sizeof(struct bcma_sflash)); ++ ++ switch (cc->capabilities & BCMA_CC_CAP_FLASHT) { ++ case BCMA_CC_FLASHT_STSER: ++ /* Probe for ST chips */ ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_DP); ++ bcma_cc_write32(cc, BCMA_CC_FLASHADDR, 0); ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_RES); ++ id = bcma_cc_read32(cc, BCMA_CC_FLASHDATA); ++ cc->sflash.blocksize = 64 * 1024; ++ switch (id) { ++ case 0x11: ++ /* ST M25P20 2 Mbit Serial Flash */ ++ cc->sflash.numblocks = 4; ++ break; ++ case 0x12: ++ /* ST M25P40 4 Mbit Serial Flash */ ++ cc->sflash.numblocks = 8; ++ break; ++ case 0x13: ++ /* ST M25P80 8 Mbit Serial Flash */ ++ cc->sflash.numblocks = 16; ++ break; ++ case 0x14: ++ /* ST M25P16 16 Mbit Serial Flash */ ++ cc->sflash.numblocks = 32; ++ break; ++ case 0x15: ++ /* ST M25P32 32 Mbit Serial Flash */ ++ cc->sflash.numblocks = 64; ++ break; ++ case 0x16: ++ /* ST M25P64 64 Mbit Serial Flash */ ++ cc->sflash.numblocks = 128; ++ break; ++ case 0x17: ++ /* ST M25FL128 128 Mbit Serial Flash */ ++ cc->sflash.numblocks = 256; ++ break; ++ case 0xbf: ++ /* All of the following flashes are SST with ++ * 4KB subsectors. Others should be added but ++ * We'll have to revamp the way we identify them ++ * since RES is not eough to disambiguate them. ++ */ ++ cc->sflash.blocksize = 4 * 1024; ++ bcma_cc_write32(cc, BCMA_CC_FLASHADDR, 1); ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_RES); ++ id2 = bcma_cc_read32(cc, BCMA_CC_FLASHDATA); ++ switch (id2) { ++ case 1: ++ /* SST25WF512 512 Kbit Serial Flash */ ++ case 0x48: ++ /* SST25VF512 512 Kbit Serial Flash */ ++ cc->sflash.numblocks = 16; ++ break; ++ case 2: ++ /* SST25WF010 1 Mbit Serial Flash */ ++ case 0x49: ++ /* SST25VF010 1 Mbit Serial Flash */ ++ cc->sflash.numblocks = 32; ++ break; ++ case 3: ++ /* SST25WF020 2 Mbit Serial Flash */ ++ case 0x43: ++ /* SST25VF020 2 Mbit Serial Flash */ ++ cc->sflash.numblocks = 64; ++ break; ++ case 4: ++ /* SST25WF040 4 Mbit Serial Flash */ ++ case 0x44: ++ /* SST25VF040 4 Mbit Serial Flash */ ++ case 0x8d: ++ /* SST25VF040B 4 Mbit Serial Flash */ ++ cc->sflash.numblocks = 128; ++ break; ++ case 5: ++ /* SST25WF080 8 Mbit Serial Flash */ ++ case 0x8e: ++ /* SST25VF080B 8 Mbit Serial Flash */ ++ cc->sflash.numblocks = 256; ++ break; ++ case 0x41: ++ /* SST25VF016 16 Mbit Serial Flash */ ++ cc->sflash.numblocks = 512; ++ break; ++ case 0x4a: ++ /* SST25VF032 32 Mbit Serial Flash */ ++ cc->sflash.numblocks = 1024; ++ break; ++ case 0x4b: ++ /* SST25VF064 64 Mbit Serial Flash */ ++ cc->sflash.numblocks = 2048; ++ break; ++ } ++ break; ++ } ++ break; ++ ++ case BCMA_CC_FLASHT_ATSER: ++ /* Probe for Atmel chips */ ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_STATUS); ++ id = bcma_cc_read32(cc, BCMA_CC_FLASHDATA) & 0x3c; ++ switch (id) { ++ case 0xc: ++ /* Atmel AT45DB011 1Mbit Serial Flash */ ++ cc->sflash.blocksize = 256; ++ cc->sflash.numblocks = 512; ++ break; ++ case 0x14: ++ /* Atmel AT45DB021 2Mbit Serial Flash */ ++ cc->sflash.blocksize = 256; ++ cc->sflash.numblocks = 1024; ++ break; ++ case 0x1c: ++ /* Atmel AT45DB041 4Mbit Serial Flash */ ++ cc->sflash.blocksize = 256; ++ cc->sflash.numblocks = 2048; ++ break; ++ case 0x24: ++ /* Atmel AT45DB081 8Mbit Serial Flash */ ++ cc->sflash.blocksize = 256; ++ cc->sflash.numblocks = 4096; ++ break; ++ case 0x2c: ++ /* Atmel AT45DB161 16Mbit Serial Flash */ ++ cc->sflash.blocksize = 512; ++ cc->sflash.numblocks = 4096; ++ break; ++ case 0x34: ++ /* Atmel AT45DB321 32Mbit Serial Flash */ ++ cc->sflash.blocksize = 512; ++ cc->sflash.numblocks = 8192; ++ break; ++ case 0x3c: ++ /* Atmel AT45DB642 64Mbit Serial Flash */ ++ cc->sflash.blocksize = 1024; ++ cc->sflash.numblocks = 8192; ++ break; ++ } ++ break; ++ } ++ ++ cc->sflash.size = cc->sflash.blocksize * cc->sflash.numblocks; ++ ++ return cc->sflash.size ? 0 : -ENODEV; ++} ++ ++/* Read len bytes starting at offset into buf. Returns number of bytes read. */ ++int bcma_sflash_read(struct bcma_drv_cc *cc, u32 offset, u32 len, u8 *buf) ++{ ++ u8 *from, *to; ++ u32 cnt, i; ++ ++ if (!len) ++ return 0; ++ ++ if ((offset + len) > cc->sflash.size) ++ return -EINVAL; ++ ++ if ((len >= 4) && (offset & 3)) ++ cnt = 4 - (offset & 3); ++ else if ((len >= 4) && ((u32)buf & 3)) ++ cnt = 4 - ((u32)buf & 3); ++ else ++ cnt = len; ++ ++ from = (u8 *)KSEG0ADDR(BCMA_FLASH2 + offset); ++ ++ to = (u8 *)buf; ++ ++ if (cnt < 4) { ++ for (i = 0; i < cnt; i++) { ++ *to = readb(from); ++ from++; ++ to++; ++ } ++ return cnt; ++ } ++ ++ while (cnt >= 4) { ++ *(u32 *)to = readl(from); ++ from += 4; ++ to += 4; ++ cnt -= 4; ++ } ++ ++ return len - cnt; ++} ++ ++/* Poll for command completion. Returns zero when complete. */ ++int bcma_sflash_poll(struct bcma_drv_cc *cc, u32 offset) ++{ ++ if (offset >= cc->sflash.size) ++ return -22; ++ ++ switch (cc->capabilities & BCMA_CC_CAP_FLASHT) { ++ case BCMA_CC_FLASHT_STSER: ++ /* Check for ST Write In Progress bit */ ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_RDSR); ++ return bcma_cc_read32(cc, BCMA_CC_FLASHDATA) ++ & BCMA_CC_FLASHDATA_ST_WIP; ++ case BCMA_CC_FLASHT_ATSER: ++ /* Check for Atmel Ready bit */ ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_STATUS); ++ return !(bcma_cc_read32(cc, BCMA_CC_FLASHDATA) ++ & BCMA_CC_FLASHDATA_AT_READY); ++ } ++ ++ return 0; ++} ++ ++ ++static int sflash_st_write(struct bcma_drv_cc *cc, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ int written = 1; ++ ++ /* Enable writes */ ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_WREN); ++ bcma_sflash_write_u8(cc, offset, *buf++); ++ /* Issue a page program with CSA bit set */ ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_CSA | BCMA_CC_FLASHCTL_ST_PP); ++ offset++; ++ len--; ++ while (len > 0) { ++ if ((offset & 255) == 0) { ++ /* Page boundary, poll droping cs and return */ ++ bcma_cc_write32(cc, BCMA_CC_FLASHCTL, 0); ++ udelay(1); ++ if (!bcma_sflash_poll(cc, offset)) { ++ /* Flash rejected command */ ++ return -EAGAIN; ++ } ++ return written; ++ } else { ++ /* Write single byte */ ++ bcma_sflash_cmd(cc, ++ BCMA_CC_FLASHCTL_ST_CSA | ++ *buf++); ++ } ++ written++; ++ offset++; ++ len--; ++ } ++ /* All done, drop cs & poll */ ++ bcma_cc_write32(cc, BCMA_CC_FLASHCTL, 0); ++ udelay(1); ++ if (!bcma_sflash_poll(cc, offset)) { ++ /* Flash rejected command */ ++ return -EAGAIN; ++ } ++ return written; ++} ++ ++static int sflash_at_write(struct bcma_drv_cc *cc, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ struct bcma_sflash *sfl = &cc->sflash; ++ u32 page, byte, mask; ++ int ret = 0; ++ ++ mask = sfl->blocksize - 1; ++ page = (offset & ~mask) << 1; ++ byte = offset & mask; ++ /* Read main memory page into buffer 1 */ ++ if (byte || (len < sfl->blocksize)) { ++ int i = 100; ++ bcma_cc_write32(cc, BCMA_CC_FLASHADDR, page); ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_BUF1_LOAD); ++ /* 250 us for AT45DB321B */ ++ while (i > 0 && bcma_sflash_poll(cc, offset)) { ++ udelay(10); ++ i--; ++ } ++ BUG_ON(!bcma_sflash_poll(cc, offset)); ++ } ++ /* Write into buffer 1 */ ++ for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) { ++ bcma_sflash_write_u8(cc, byte++, *buf++); ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_BUF1_WRITE); ++ } ++ /* Write buffer 1 into main memory page */ ++ bcma_cc_write32(cc, BCMA_CC_FLASHADDR, page); ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_BUF1_PROGRAM); ++ ++ return ret; ++} ++ ++/* Write len bytes starting at offset into buf. Returns number of bytes ++ * written. Caller should poll for completion. ++ */ ++int bcma_sflash_write(struct bcma_drv_cc *cc, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ struct bcma_sflash *sfl; ++ int ret = 0, tries = NUM_RETRIES; ++ ++ if (!len) ++ return 0; ++ ++ if ((offset + len) > cc->sflash.size) ++ return -EINVAL; ++ ++ sfl = &cc->sflash; ++ switch (cc->capabilities & BCMA_CC_CAP_FLASHT) { ++ case BCMA_CC_FLASHT_STSER: ++ do { ++ ret = sflash_st_write(cc, offset, len, buf); ++ tries--; ++ } while (ret == -EAGAIN && tries > 0); ++ ++ if (ret == -EAGAIN && tries == 0) { ++ pr_info("ST Flash rejected write\n"); ++ ret = -EIO; ++ } ++ break; ++ case BCMA_CC_FLASHT_ATSER: ++ ret = sflash_at_write(cc, offset, len, buf); ++ break; ++ } ++ ++ return ret; ++} ++ ++/* Erase a region. Returns number of bytes scheduled for erasure. ++ * Caller should poll for completion. ++ */ ++int bcma_sflash_erase(struct bcma_drv_cc *cc, u32 offset) ++{ ++ struct bcma_sflash *sfl; ++ ++ if (offset >= cc->sflash.size) ++ return -EINVAL; ++ ++ sfl = &cc->sflash; ++ switch (cc->capabilities & BCMA_CC_CAP_FLASHT) { ++ case BCMA_CC_FLASHT_STSER: ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_WREN); ++ bcma_cc_write32(cc, BCMA_CC_FLASHADDR, offset); ++ /* Newer flashes have "sub-sectors" which can be erased independently ++ * with a new command: ST_SSE. The ST_SE command erases 64KB just as ++ * before. ++ */ ++ if (sfl->blocksize < (64 * 1024)) ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_SSE); ++ else ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_SE); ++ return sfl->blocksize; ++ case BCMA_CC_FLASHT_ATSER: ++ bcma_cc_write32(cc, BCMA_CC_FLASHADDR, offset << 1); ++ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_AT_PAGE_ERASE); ++ return sfl->blocksize; ++ } ++ ++ return 0; ++} +--- a/drivers/bcma/driver_mips.c ++++ b/drivers/bcma/driver_mips.c +@@ -185,7 +185,13 @@ static void bcma_core_mips_flash_detect( + switch (bus->drv_cc.capabilities & BCMA_CC_CAP_FLASHT) { + case BCMA_CC_FLASHT_STSER: + case BCMA_CC_FLASHT_ATSER: +- pr_err("Serial flash not supported.\n"); ++#ifdef CONFIG_BCMA_SFLASH ++ pr_info("found serial flash.\n"); ++ bus->drv_cc.flash_type = BCMA_SFLASH; ++ bcma_sflash_init(&bus->drv_cc); ++#else ++ pr_info("serial flash not supported.\n"); ++#endif /* CONFIG_BCMA_SFLASH */ + break; + case BCMA_CC_FLASHT_PARA: + pr_info("found parallel flash.\n"); +--- a/include/linux/bcma/bcma_driver_chipcommon.h ++++ b/include/linux/bcma/bcma_driver_chipcommon.h +@@ -399,6 +399,7 @@ struct bcma_chipcommon_pmu { + #ifdef CONFIG_BCMA_DRIVER_MIPS + enum bcma_flash_type { + BCMA_PFLASH, ++ BCMA_SFLASH, + }; + + struct bcma_pflash { +@@ -407,6 +408,14 @@ struct bcma_pflash { + u32 window_size; + }; + ++#ifdef CONFIG_BCMA_SFLASH ++struct bcma_sflash { ++ u32 blocksize; /* Block size */ ++ u32 numblocks; /* Number of blocks */ ++ u32 size; /* Total size in bytes */ ++}; ++#endif /* CONFIG_BCMA_SFLASH */ ++ + struct bcma_serial_port { + void *regs; + unsigned long clockspeed; +@@ -429,6 +438,9 @@ struct bcma_drv_cc { + enum bcma_flash_type flash_type; + union { + struct bcma_pflash pflash; ++#ifdef CONFIG_BCMA_SFLASH ++ struct bcma_sflash sflash; ++#endif /* CONFIG_BCMA_SFLASH */ + }; + + int nr_serial_ports; +@@ -483,4 +495,14 @@ extern void bcma_chipco_chipctl_maskset( + extern void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, + u32 offset, u32 mask, u32 set); + ++#ifdef CONFIG_BCMA_SFLASH ++/* Chipcommon sflash support. */ ++int bcma_sflash_read(struct bcma_drv_cc *cc, u32 offset, u32 len, ++ u8 *buf); ++int bcma_sflash_poll(struct bcma_drv_cc *cc, u32 offset); ++int bcma_sflash_write(struct bcma_drv_cc *cc, u32 offset, u32 len, ++ const u8 *buf); ++int bcma_sflash_erase(struct bcma_drv_cc *cc, u32 offset); ++#endif /* CONFIG_BCMA_SFLASH */ ++ + #endif /* LINUX_BCMA_DRIVER_CC_H_ */ diff --git a/target/linux/brcm47xx/patches-3.3/022-ssb-move-flash-to-chipcommon.patch b/target/linux/brcm47xx/patches-3.3/022-ssb-move-flash-to-chipcommon.patch new file mode 100644 index 0000000000..400de8988c --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/022-ssb-move-flash-to-chipcommon.patch @@ -0,0 +1,134 @@ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -27,7 +27,7 @@ static char nvram_buf[NVRAM_SPACE]; + static void early_nvram_init(void) + { + #ifdef CONFIG_BCM47XX_SSB +- struct ssb_mipscore *mcore_ssb; ++ struct ssb_chipcommon *ssb_cc; + #endif + #ifdef CONFIG_BCM47XX_BCMA + struct bcma_drv_cc *bcma_cc; +@@ -42,9 +42,9 @@ static void early_nvram_init(void) + switch (bcm47xx_bus_type) { + #ifdef CONFIG_BCM47XX_SSB + case BCM47XX_BUS_TYPE_SSB: +- mcore_ssb = &bcm47xx_bus.ssb.mipscore; +- base = mcore_ssb->flash_window; +- lim = mcore_ssb->flash_window_size; ++ ssb_cc = &bcm47xx_bus.ssb.chipco; ++ base = ssb_cc->pflash.window; ++ lim = ssb_cc->pflash.window_size; + break; + #endif + #ifdef CONFIG_BCM47XX_BCMA +--- a/arch/mips/bcm47xx/wgt634u.c ++++ b/arch/mips/bcm47xx/wgt634u.c +@@ -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/ssb/driver_mipscore.c ++++ b/drivers/ssb/driver_mipscore.c +@@ -190,16 +190,34 @@ static void ssb_mips_flash_detect(struct + { + struct ssb_bus *bus = mcore->dev->bus; + +- mcore->flash_buswidth = 2; +- if (bus->chipco.dev) { +- mcore->flash_window = 0x1c000000; +- mcore->flash_window_size = 0x02000000; ++ /* When there is no chipcommon on the bus there is 4MB flash */ ++ if (!bus->chipco.dev) { ++ pr_info("found parallel flash.\n"); ++ bus->chipco.flash_type = SSB_PFLASH; ++ bus->chipco.pflash.window = SSB_FLASH1; ++ bus->chipco.pflash.window_size = SSB_FLASH1_SZ; ++ bus->chipco.pflash.buswidth = 2; ++ return; ++ } ++ ++ switch (bus->chipco.capabilities & SSB_CHIPCO_CAP_FLASHT) { ++ case SSB_CHIPCO_FLASHT_STSER: ++ case SSB_CHIPCO_FLASHT_ATSER: ++ pr_info("serial flash not supported.\n"); ++ break; ++ case SSB_CHIPCO_FLASHT_PARA: ++ pr_info("found parallel flash.\n"); ++ bus->chipco.flash_type = SSB_PFLASH; ++ bus->chipco.pflash.window = SSB_FLASH2; ++ bus->chipco.pflash.window_size = SSB_FLASH2_SZ; + if ((ssb_read32(bus->chipco.dev, SSB_CHIPCO_FLASH_CFG) +- & SSB_CHIPCO_CFG_DS16) == 0) +- mcore->flash_buswidth = 1; +- } else { +- mcore->flash_window = 0x1fc00000; +- mcore->flash_window_size = 0x00400000; ++ & SSB_CHIPCO_CFG_DS16) == 0) ++ bus->chipco.pflash.buswidth = 1; ++ else ++ bus->chipco.pflash.buswidth = 2; ++ break; ++ default: ++ pr_err("flash not supported.\n"); + } + } + +--- a/include/linux/ssb/ssb_driver_chipcommon.h ++++ b/include/linux/ssb/ssb_driver_chipcommon.h +@@ -582,6 +582,18 @@ struct ssb_chipcommon_pmu { + u32 crystalfreq; /* The active crystal frequency (in kHz) */ + }; + ++#ifdef CONFIG_SSB_DRIVER_MIPS ++enum ssb_flash_type { ++ SSB_PFLASH, ++}; ++ ++struct ssb_pflash { ++ u8 buswidth; ++ u32 window; ++ u32 window_size; ++}; ++#endif /* CONFIG_SSB_DRIVER_MIPS */ ++ + struct ssb_chipcommon { + struct ssb_device *dev; + u32 capabilities; +@@ -589,6 +601,12 @@ struct ssb_chipcommon { + /* Fast Powerup Delay constant */ + u16 fast_pwrup_delay; + struct ssb_chipcommon_pmu pmu; ++#ifdef CONFIG_SSB_DRIVER_MIPS ++ enum ssb_flash_type flash_type; ++ union { ++ struct ssb_pflash pflash; ++ }; ++#endif /* CONFIG_SSB_DRIVER_MIPS */ + }; + + static inline bool ssb_chipco_available(struct ssb_chipcommon *cc) +--- a/include/linux/ssb/ssb_driver_mips.h ++++ b/include/linux/ssb/ssb_driver_mips.h +@@ -19,10 +19,6 @@ struct ssb_mipscore { + + int nr_serial_ports; + struct ssb_serial_port serial_ports[4]; +- +- u8 flash_buswidth; +- u32 flash_window; +- u32 flash_window_size; + }; + + extern void ssb_mipscore_init(struct ssb_mipscore *mcore); diff --git a/target/linux/brcm47xx/patches-3.3/023-ssb-add-serial-flash-support.patch b/target/linux/brcm47xx/patches-3.3/023-ssb-add-serial-flash-support.patch new file mode 100644 index 0000000000..ba5a5c2d1b --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/023-ssb-add-serial-flash-support.patch @@ -0,0 +1,573 @@ +--- a/drivers/ssb/Kconfig ++++ b/drivers/ssb/Kconfig +@@ -137,6 +137,12 @@ config SSB_DRIVER_MIPS + + If unsure, say N + ++config SSB_SFLASH ++ bool ++ depends on SSB_DRIVER_MIPS ++ default y ++ ++ + # Assumption: We are on embedded, if we compile the MIPS core. + config SSB_EMBEDDED + bool +--- a/drivers/ssb/Makefile ++++ b/drivers/ssb/Makefile +@@ -11,6 +11,7 @@ ssb-$(CONFIG_SSB_SDIOHOST) += sdio.o + # built-in drivers + ssb-y += driver_chipcommon.o + ssb-y += driver_chipcommon_pmu.o ++ssb-$(CONFIG_SSB_SFLASH) += driver_chipcommon_sflash.o + ssb-$(CONFIG_SSB_DRIVER_MIPS) += driver_mipscore.o + ssb-$(CONFIG_SSB_DRIVER_EXTIF) += driver_extif.o + ssb-$(CONFIG_SSB_DRIVER_PCICORE) += driver_pcicore.o +--- /dev/null ++++ b/drivers/ssb/driver_chipcommon_sflash.c +@@ -0,0 +1,451 @@ ++/* ++ * Broadcom SiliconBackplane chipcommon serial flash interface ++ * ++ * Copyright 2011, Jonas Gorski ++ * Copyright 2011, 2012, Hauke Mehrtens ++ * Copyright 2010, Broadcom Corporation ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++ ++#include ++#include ++#include ++ ++#include "ssb_private.h" ++ ++#define NUM_RETRIES 3 ++ ++ ++/* Issue a serial flash command */ ++static inline void ssb_sflash_cmd(struct ssb_chipcommon *cc, u32 opcode) ++{ ++ chipco_write32(cc, SSB_CHIPCO_FLASHCTL, ++ SSB_CHIPCO_FLASHCTL_START | opcode); ++ while (chipco_read32(cc, SSB_CHIPCO_FLASHCTL) ++ & SSB_CHIPCO_FLASHCTL_BUSY) ++ ; ++} ++ ++ ++static inline void ssb_sflash_write_u8(struct ssb_chipcommon *cc, ++ u32 offset, u8 byte) ++{ ++ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, offset); ++ chipco_write32(cc, SSB_CHIPCO_FLASHDATA, byte); ++} ++ ++/* Initialize serial flash access */ ++int ssb_sflash_init(struct ssb_chipcommon *cc) ++{ ++ u32 id, id2; ++ ++ memset(&cc->sflash, 0, sizeof(struct ssb_sflash)); ++ ++ switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) { ++ case SSB_CHIPCO_FLASHT_STSER: ++ /* Probe for ST chips */ ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_DP); ++ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, 0); ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_RES); ++ id = chipco_read32(cc, SSB_CHIPCO_FLASHDATA); ++ cc->sflash.blocksize = 64 * 1024; ++ switch (id) { ++ case 0x11: ++ /* ST M25P20 2 Mbit Serial Flash */ ++ cc->sflash.numblocks = 4; ++ break; ++ case 0x12: ++ /* ST M25P40 4 Mbit Serial Flash */ ++ cc->sflash.numblocks = 8; ++ break; ++ case 0x13: ++ /* ST M25P80 8 Mbit Serial Flash */ ++ cc->sflash.numblocks = 16; ++ break; ++ case 0x14: ++ /* ST M25P16 16 Mbit Serial Flash */ ++ cc->sflash.numblocks = 32; ++ break; ++ case 0x15: ++ /* ST M25P32 32 Mbit Serial Flash */ ++ cc->sflash.numblocks = 64; ++ break; ++ case 0x16: ++ /* ST M25P64 64 Mbit Serial Flash */ ++ cc->sflash.numblocks = 128; ++ break; ++ case 0x17: ++ /* ST M25FL128 128 Mbit Serial Flash */ ++ cc->sflash.numblocks = 256; ++ break; ++ case 0xbf: ++ /* All of the following flashes are SST with ++ * 4KB subsectors. Others should be added but ++ * We'll have to revamp the way we identify them ++ * since RES is not eough to disambiguate them. ++ */ ++ cc->sflash.blocksize = 4 * 1024; ++ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, 1); ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_RES); ++ id2 = chipco_read32(cc, SSB_CHIPCO_FLASHDATA); ++ switch (id2) { ++ case 1: ++ /* SST25WF512 512 Kbit Serial Flash */ ++ case 0x48: ++ /* SST25VF512 512 Kbit Serial Flash */ ++ cc->sflash.numblocks = 16; ++ break; ++ case 2: ++ /* SST25WF010 1 Mbit Serial Flash */ ++ case 0x49: ++ /* SST25VF010 1 Mbit Serial Flash */ ++ cc->sflash.numblocks = 32; ++ break; ++ case 3: ++ /* SST25WF020 2 Mbit Serial Flash */ ++ case 0x43: ++ /* SST25VF020 2 Mbit Serial Flash */ ++ cc->sflash.numblocks = 64; ++ break; ++ case 4: ++ /* SST25WF040 4 Mbit Serial Flash */ ++ case 0x44: ++ /* SST25VF040 4 Mbit Serial Flash */ ++ case 0x8d: ++ /* SST25VF040B 4 Mbit Serial Flash */ ++ cc->sflash.numblocks = 128; ++ break; ++ case 5: ++ /* SST25WF080 8 Mbit Serial Flash */ ++ case 0x8e: ++ /* SST25VF080B 8 Mbit Serial Flash */ ++ cc->sflash.numblocks = 256; ++ break; ++ case 0x41: ++ /* SST25VF016 16 Mbit Serial Flash */ ++ cc->sflash.numblocks = 512; ++ break; ++ case 0x4a: ++ /* SST25VF032 32 Mbit Serial Flash */ ++ cc->sflash.numblocks = 1024; ++ break; ++ case 0x4b: ++ /* SST25VF064 64 Mbit Serial Flash */ ++ cc->sflash.numblocks = 2048; ++ break; ++ } ++ break; ++ } ++ break; ++ ++ case SSB_CHIPCO_FLASHT_ATSER: ++ /* Probe for Atmel chips */ ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_STATUS); ++ id = chipco_read32(cc, SSB_CHIPCO_FLASHDATA) & 0x3c; ++ switch (id) { ++ case 0xc: ++ /* Atmel AT45DB011 1Mbit Serial Flash */ ++ cc->sflash.blocksize = 256; ++ cc->sflash.numblocks = 512; ++ break; ++ case 0x14: ++ /* Atmel AT45DB021 2Mbit Serial Flash */ ++ cc->sflash.blocksize = 256; ++ cc->sflash.numblocks = 1024; ++ break; ++ case 0x1c: ++ /* Atmel AT45DB041 4Mbit Serial Flash */ ++ cc->sflash.blocksize = 256; ++ cc->sflash.numblocks = 2048; ++ break; ++ case 0x24: ++ /* Atmel AT45DB081 8Mbit Serial Flash */ ++ cc->sflash.blocksize = 256; ++ cc->sflash.numblocks = 4096; ++ break; ++ case 0x2c: ++ /* Atmel AT45DB161 16Mbit Serial Flash */ ++ cc->sflash.blocksize = 512; ++ cc->sflash.numblocks = 4096; ++ break; ++ case 0x34: ++ /* Atmel AT45DB321 32Mbit Serial Flash */ ++ cc->sflash.blocksize = 512; ++ cc->sflash.numblocks = 8192; ++ break; ++ case 0x3c: ++ /* Atmel AT45DB642 64Mbit Serial Flash */ ++ cc->sflash.blocksize = 1024; ++ cc->sflash.numblocks = 8192; ++ break; ++ } ++ break; ++ } ++ ++ cc->sflash.size = cc->sflash.blocksize * cc->sflash.numblocks; ++ ++ return cc->sflash.size ? 0 : -ENODEV; ++} ++ ++/* Read len bytes starting at offset into buf. Returns number of bytes read. */ ++int ssb_sflash_read(struct ssb_chipcommon *cc, u32 offset, u32 len, u8 *buf) ++{ ++ u8 *from, *to; ++ u32 cnt, i; ++ ++ if (!len) ++ return 0; ++ ++ if ((offset + len) > cc->sflash.size) ++ return -EINVAL; ++ ++ if ((len >= 4) && (offset & 3)) ++ cnt = 4 - (offset & 3); ++ else if ((len >= 4) && ((u32)buf & 3)) ++ cnt = 4 - ((u32)buf & 3); ++ else ++ cnt = len; ++ ++ ++ if (cc->dev->id.revision == 12) ++ from = (u8 *)KSEG1ADDR(SSB_FLASH2 + offset); ++ else ++ from = (u8 *)KSEG0ADDR(SSB_FLASH2 + offset); ++ ++ to = (u8 *)buf; ++ ++ if (cnt < 4) { ++ for (i = 0; i < cnt; i++) { ++ *to = readb(from); ++ from++; ++ to++; ++ } ++ return cnt; ++ } ++ ++ while (cnt >= 4) { ++ *(u32 *)to = readl(from); ++ from += 4; ++ to += 4; ++ cnt -= 4; ++ } ++ ++ return len - cnt; ++} ++ ++/* Poll for command completion. Returns zero when complete. */ ++int ssb_sflash_poll(struct ssb_chipcommon *cc, u32 offset) ++{ ++ if (offset >= cc->sflash.size) ++ return -22; ++ ++ switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) { ++ case SSB_CHIPCO_FLASHT_STSER: ++ /* Check for ST Write In Progress bit */ ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_RDSR); ++ return chipco_read32(cc, SSB_CHIPCO_FLASHDATA) ++ & SSB_CHIPCO_FLASHSTA_ST_WIP; ++ case SSB_CHIPCO_FLASHT_ATSER: ++ /* Check for Atmel Ready bit */ ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_STATUS); ++ return !(chipco_read32(cc, SSB_CHIPCO_FLASHDATA) ++ & SSB_CHIPCO_FLASHSTA_AT_READY); ++ } ++ ++ return 0; ++} ++ ++ ++static int sflash_st_write(struct ssb_chipcommon *cc, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ struct ssb_bus *bus = cc->dev->bus; ++ int ret = 0; ++ bool is4712b0 = (bus->chip_id == 0x4712) && (bus->chip_rev == 3); ++ u32 mask; ++ ++ /* Enable writes */ ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_WREN); ++ if (is4712b0) { ++ mask = 1 << 14; ++ ssb_sflash_write_u8(cc, offset, *buf++); ++ /* Set chip select */ ++ chipco_set32(cc, SSB_CHIPCO_GPIOOUT, mask); ++ /* Issue a page program with the first byte */ ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_PP); ++ ret = 1; ++ offset++; ++ len--; ++ while (len > 0) { ++ if ((offset & 255) == 0) { ++ /* Page boundary, drop cs and return */ ++ chipco_mask32(cc, SSB_CHIPCO_GPIOOUT, ~mask); ++ udelay(1); ++ if (!ssb_sflash_poll(cc, offset)) { ++ /* Flash rejected command */ ++ return -EAGAIN; ++ } ++ return ret; ++ } else { ++ /* Write single byte */ ++ ssb_sflash_cmd(cc, *buf++); ++ } ++ ret++; ++ offset++; ++ len--; ++ } ++ /* All done, drop cs */ ++ chipco_mask32(cc, SSB_CHIPCO_GPIOOUT, ~mask); ++ udelay(1); ++ if (!ssb_sflash_poll(cc, offset)) { ++ /* Flash rejected command */ ++ return -EAGAIN; ++ } ++ } else if (cc->dev->id.revision >= 20) { ++ ssb_sflash_write_u8(cc, offset, *buf++); ++ /* Issue a page program with CSA bit set */ ++ ssb_sflash_cmd(cc, ++ SSB_CHIPCO_FLASHCTL_ST_CSA | ++ SSB_CHIPCO_FLASHCTL_ST_PP); ++ ret = 1; ++ offset++; ++ len--; ++ while (len > 0) { ++ if ((offset & 255) == 0) { ++ /* Page boundary, poll droping cs and return */ ++ chipco_write32(cc, SSB_CHIPCO_FLASHCTL, 0); ++ udelay(1); ++ if (!ssb_sflash_poll(cc, offset)) { ++ /* Flash rejected command */ ++ return -EAGAIN; ++ } ++ return ret; ++ } else { ++ /* Write single byte */ ++ ssb_sflash_cmd(cc, ++ SSB_CHIPCO_FLASHCTL_ST_CSA | ++ *buf++); ++ } ++ ret++; ++ offset++; ++ len--; ++ } ++ /* All done, drop cs & poll */ ++ chipco_write32(cc, SSB_CHIPCO_FLASHCTL, 0); ++ udelay(1); ++ if (!ssb_sflash_poll(cc, offset)) { ++ /* Flash rejected command */ ++ return -EAGAIN; ++ } ++ } else { ++ ret = 1; ++ ssb_sflash_write_u8(cc, offset, *buf); ++ /* Page program */ ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_PP); ++ } ++ return ret; ++} ++ ++static int sflash_at_write(struct ssb_chipcommon *cc, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ struct ssb_sflash *sfl = &cc->sflash; ++ u32 page, byte, mask; ++ int ret = 0; ++ mask = sfl->blocksize - 1; ++ page = (offset & ~mask) << 1; ++ byte = offset & mask; ++ /* Read main memory page into buffer 1 */ ++ if (byte || (len < sfl->blocksize)) { ++ int i = 100; ++ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, page); ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_BUF1_LOAD); ++ /* 250 us for AT45DB321B */ ++ while (i > 0 && ssb_sflash_poll(cc, offset)) { ++ udelay(10); ++ i--; ++ } ++ BUG_ON(!ssb_sflash_poll(cc, offset)); ++ } ++ /* Write into buffer 1 */ ++ for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) { ++ ssb_sflash_write_u8(cc, byte++, *buf++); ++ ssb_sflash_cmd(cc, ++ SSB_CHIPCO_FLASHCTL_AT_BUF1_WRITE); ++ } ++ /* Write buffer 1 into main memory page */ ++ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, page); ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_BUF1_PROGRAM); ++ ++ return ret; ++} ++ ++/* Write len bytes starting at offset into buf. Returns number of bytes ++ * written. Caller should poll for completion. ++ */ ++int ssb_sflash_write(struct ssb_chipcommon *cc, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ struct ssb_sflash *sfl; ++ int ret = 0, tries = NUM_RETRIES; ++ ++ if (!len) ++ return 0; ++ ++ if ((offset + len) > cc->sflash.size) ++ return -EINVAL; ++ ++ sfl = &cc->sflash; ++ switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) { ++ case SSB_CHIPCO_FLASHT_STSER: ++ do { ++ ret = sflash_st_write(cc, offset, len, buf); ++ tries--; ++ } while (ret == -EAGAIN && tries > 0); ++ ++ if (ret == -EAGAIN && tries == 0) { ++ pr_info("ST Flash rejected write\n"); ++ ret = -EIO; ++ } ++ break; ++ case SSB_CHIPCO_FLASHT_ATSER: ++ ret = sflash_at_write(cc, offset, len, buf); ++ break; ++ } ++ ++ return ret; ++} ++ ++/* Erase a region. Returns number of bytes scheduled for erasure. ++ * Caller should poll for completion. ++ */ ++int ssb_sflash_erase(struct ssb_chipcommon *cc, u32 offset) ++{ ++ struct ssb_sflash *sfl; ++ ++ if (offset >= cc->sflash.size) ++ return -EINVAL; ++ ++ sfl = &cc->sflash; ++ switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) { ++ case SSB_CHIPCO_FLASHT_STSER: ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_WREN); ++ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, offset); ++ /* Newer flashes have "sub-sectors" which can be erased ++ * independently with a new command: ST_SSE. The ST_SE command ++ * erases 64KB just as before. ++ */ ++ if (sfl->blocksize < (64 * 1024)) ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_SSE); ++ else ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_SE); ++ return sfl->blocksize; ++ case SSB_CHIPCO_FLASHT_ATSER: ++ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, offset << 1); ++ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_PAGE_ERASE); ++ return sfl->blocksize; ++ } ++ ++ return 0; ++} +--- a/drivers/ssb/driver_mipscore.c ++++ b/drivers/ssb/driver_mipscore.c +@@ -203,7 +203,13 @@ static void ssb_mips_flash_detect(struct + switch (bus->chipco.capabilities & SSB_CHIPCO_CAP_FLASHT) { + case SSB_CHIPCO_FLASHT_STSER: + case SSB_CHIPCO_FLASHT_ATSER: ++#ifdef CONFIG_SSB_SFLASH ++ pr_info("found serial flash.\n"); ++ bus->chipco.flash_type = SSB_SFLASH; ++ ssb_sflash_init(&bus->chipco); ++#else + pr_info("serial flash not supported.\n"); ++#endif /* CONFIG_SSB_SFLASH */ + break; + case SSB_CHIPCO_FLASHT_PARA: + pr_info("found parallel flash.\n"); +--- a/drivers/ssb/ssb_private.h ++++ b/drivers/ssb/ssb_private.h +@@ -192,6 +192,10 @@ extern int ssb_devices_freeze(struct ssb + extern int ssb_devices_thaw(struct ssb_freeze_context *ctx); + + ++#ifdef CONFIG_SSB_SFLASH ++/* driver_chipcommon_sflash.c */ ++int ssb_sflash_init(struct ssb_chipcommon *cc); ++#endif /* CONFIG_SSB_SFLASH */ + + /* b43_pci_bridge.c */ + #ifdef CONFIG_SSB_B43_PCI_BRIDGE +--- a/include/linux/ssb/ssb_driver_chipcommon.h ++++ b/include/linux/ssb/ssb_driver_chipcommon.h +@@ -503,8 +503,10 @@ + #define SSB_CHIPCO_FLASHCTL_ST_PP 0x0302 /* Page Program */ + #define SSB_CHIPCO_FLASHCTL_ST_SE 0x02D8 /* Sector Erase */ + #define SSB_CHIPCO_FLASHCTL_ST_BE 0x00C7 /* Bulk Erase */ +-#define SSB_CHIPCO_FLASHCTL_ST_DP 0x00B9 /* Deep Power-down */ +-#define SSB_CHIPCO_FLASHCTL_ST_RSIG 0x03AB /* Read Electronic Signature */ ++#define SSB_CHIPCO_FLASHCTL_ST_DP 0x00D9 /* Deep Power-down */ ++#define SSB_CHIPCO_FLASHCTL_ST_RES 0x03AB /* Read Electronic Signature */ ++#define SSB_CHIPCO_FLASHCTL_ST_CSA 0x1000 /* Keep chip select asserted */ ++#define SSB_CHIPCO_FLASHCTL_ST_SSE 0x0220 /* Sub-sector Erase */ + + /* Status register bits for ST flashes */ + #define SSB_CHIPCO_FLASHSTA_ST_WIP 0x01 /* Write In Progress */ +@@ -585,6 +587,7 @@ struct ssb_chipcommon_pmu { + #ifdef CONFIG_SSB_DRIVER_MIPS + enum ssb_flash_type { + SSB_PFLASH, ++ SSB_SFLASH, + }; + + struct ssb_pflash { +@@ -592,6 +595,14 @@ struct ssb_pflash { + u32 window; + u32 window_size; + }; ++ ++#ifdef CONFIG_SSB_SFLASH ++struct ssb_sflash { ++ u32 blocksize; /* Block size */ ++ u32 numblocks; /* Number of blocks */ ++ u32 size; /* Total size in bytes */ ++}; ++#endif /* CONFIG_SSB_SFLASH */ + #endif /* CONFIG_SSB_DRIVER_MIPS */ + + struct ssb_chipcommon { +@@ -605,6 +616,9 @@ struct ssb_chipcommon { + enum ssb_flash_type flash_type; + union { + struct ssb_pflash pflash; ++#ifdef CONFIG_SSB_SFLASH ++ struct ssb_sflash sflash; ++#endif /* CONFIG_SSB_SFLASH */ + }; + #endif /* CONFIG_SSB_DRIVER_MIPS */ + }; +@@ -666,6 +680,16 @@ extern int ssb_chipco_serial_init(struct + struct ssb_serial_port *ports); + #endif /* CONFIG_SSB_SERIAL */ + ++#ifdef CONFIG_SSB_SFLASH ++/* Chipcommon sflash support. */ ++int ssb_sflash_read(struct ssb_chipcommon *cc, u32 offset, u32 len, ++ u8 *buf); ++int ssb_sflash_poll(struct ssb_chipcommon *cc, u32 offset); ++int ssb_sflash_write(struct ssb_chipcommon *cc, u32 offset, u32 len, ++ const u8 *buf); ++int ssb_sflash_erase(struct ssb_chipcommon *cc, u32 offset); ++#endif /* CONFIG_SSB_SFLASH */ ++ + /* PMU support */ + extern void ssb_pmu_init(struct ssb_chipcommon *cc); + diff --git a/target/linux/brcm47xx/patches-3.3/024-brcm47xx-add-common-interface-for-sflash.patch b/target/linux/brcm47xx/patches-3.3/024-brcm47xx-add-common-interface-for-sflash.patch new file mode 100644 index 0000000000..9839ef8f40 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/024-brcm47xx-add-common-interface-for-sflash.patch @@ -0,0 +1,164 @@ +--- a/arch/mips/bcm47xx/Makefile ++++ b/arch/mips/bcm47xx/Makefile +@@ -3,5 +3,5 @@ + # under Linux. + # + +-obj-y += gpio.o irq.o nvram.o prom.o serial.o setup.o time.o ++obj-y += gpio.o irq.o nvram.o prom.o serial.o setup.o time.o bus.o + obj-$(CONFIG_BCM47XX_SSB) += wgt634u.o +--- /dev/null ++++ b/arch/mips/bcm47xx/bus.c +@@ -0,0 +1,82 @@ ++/* ++ * BCM947xx nvram variable access ++ * ++ * Copyright (C) 2011 Hauke Mehrtens ++ * ++ * 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 ++ ++static int bcm47xx_sflash_bcma_read(struct bcm47xx_sflash *dev, u32 offset, u32 len, u8 *buf) ++{ ++ return bcma_sflash_read(dev->bcc, offset, len, buf); ++} ++ ++static int bcm47xx_sflash_bcma_poll(struct bcm47xx_sflash *dev, u32 offset) ++{ ++ return bcma_sflash_poll(dev->bcc, offset); ++} ++ ++static int bcm47xx_sflash_bcma_write(struct bcm47xx_sflash *dev, u32 offset, u32 len, const u8 *buf) ++{ ++ return bcma_sflash_write(dev->bcc, offset, len, buf); ++} ++ ++static int bcm47xx_sflash_bcma_erase(struct bcm47xx_sflash *dev, u32 offset) ++{ ++ return bcma_sflash_erase(dev->bcc, offset); ++} ++ ++void bcm47xx_sflash_struct_bcma_init(struct bcm47xx_sflash *sflash, struct bcma_drv_cc *bcc) ++{ ++ sflash->sflash_type = BCM47XX_BUS_TYPE_BCMA; ++ sflash->bcc = bcc; ++ ++ sflash->read = bcm47xx_sflash_bcma_read; ++ sflash->poll = bcm47xx_sflash_bcma_poll; ++ sflash->write = bcm47xx_sflash_bcma_write; ++ sflash->erase = bcm47xx_sflash_bcma_erase; ++ ++ sflash->blocksize = bcc->sflash.blocksize; ++ sflash->numblocks = bcc->sflash.numblocks; ++ sflash->size = bcc->sflash.size; ++} ++ ++static int bcm47xx_sflash_ssb_read(struct bcm47xx_sflash *dev, u32 offset, u32 len, u8 *buf) ++{ ++ return ssb_sflash_read(dev->scc, offset, len, buf); ++} ++ ++static int bcm47xx_sflash_ssb_poll(struct bcm47xx_sflash *dev, u32 offset) ++{ ++ return ssb_sflash_poll(dev->scc, offset); ++} ++ ++static int bcm47xx_sflash_ssb_write(struct bcm47xx_sflash *dev, u32 offset, u32 len, const u8 *buf) ++{ ++ return ssb_sflash_write(dev->scc, offset, len, buf); ++} ++ ++static int bcm47xx_sflash_ssb_erase(struct bcm47xx_sflash *dev, u32 offset) ++{ ++ return ssb_sflash_erase(dev->scc, offset); ++} ++ ++void bcm47xx_sflash_struct_ssb_init(struct bcm47xx_sflash *sflash, struct ssb_chipcommon *scc) ++{ ++ sflash->sflash_type = BCM47XX_BUS_TYPE_SSB; ++ sflash->scc = scc; ++ ++ sflash->read = bcm47xx_sflash_ssb_read; ++ sflash->poll = bcm47xx_sflash_ssb_poll; ++ sflash->write = bcm47xx_sflash_ssb_write; ++ sflash->erase = bcm47xx_sflash_ssb_erase; ++ ++ sflash->blocksize = scc->sflash.blocksize; ++ sflash->numblocks = scc->sflash.numblocks; ++ sflash->size = scc->sflash.size; ++} +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -43,6 +43,8 @@ EXPORT_SYMBOL(bcm47xx_bus); + enum bcm47xx_bus_type bcm47xx_bus_type; + EXPORT_SYMBOL(bcm47xx_bus_type); + ++struct bcm47xx_sflash bcm47xx_sflash; ++ + static void bcm47xx_machine_restart(char *command) + { + printk(KERN_ALERT "Please stand by while rebooting the system...\n"); +@@ -291,6 +293,9 @@ static void __init bcm47xx_register_ssb( + if (err) + panic("Failed to initialize SSB bus (err %d)", err); + ++ if (bcm47xx_bus.ssb.chipco.flash_type == SSB_SFLASH) ++ bcm47xx_sflash_struct_ssb_init(&bcm47xx_sflash, &bcm47xx_bus.ssb.chipco); ++ + mcore = &bcm47xx_bus.ssb.mipscore; + if (nvram_getenv("kernel_args", buf, sizeof(buf)) >= 0) { + if (strstr(buf, "console=ttyS1")) { +@@ -315,6 +320,9 @@ static void __init bcm47xx_register_bcma + err = bcma_host_soc_register(&bcm47xx_bus.bcma); + if (err) + panic("Failed to initialize BCMA bus (err %d)", err); ++ ++ if (bcm47xx_bus.bcma.bus.drv_cc.flash_type == BCMA_SFLASH) ++ bcm47xx_sflash_struct_bcma_init(&bcm47xx_sflash, &bcm47xx_bus.bcma.bus.drv_cc); + } + #endif + +--- /dev/null ++++ b/arch/mips/include/asm/mach-bcm47xx/bus.h +@@ -0,0 +1,36 @@ ++/* ++ * BCM947xx nvram variable access ++ * ++ * Copyright (C) 2011 Hauke Mehrtens ++ * ++ * 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 ++#include ++#include ++ ++struct bcm47xx_sflash { ++ enum bcm47xx_bus_type sflash_type; ++ union { ++ struct ssb_chipcommon *scc; ++ struct bcma_drv_cc *bcc; ++ }; ++ ++ int (*read)(struct bcm47xx_sflash *dev, u32 offset, u32 len, u8 *buf); ++ int (*poll)(struct bcm47xx_sflash *dev, u32 offset); ++ int (*write)(struct bcm47xx_sflash *dev, u32 offset, u32 len, const u8 *buf); ++ int (*erase)(struct bcm47xx_sflash *dev, u32 offset); ++ ++ u32 blocksize; /* Block size */ ++ u32 numblocks; /* Number of blocks */ ++ u32 size; /* Total size in bytes */ ++}; ++ ++void bcm47xx_sflash_struct_bcma_init(struct bcm47xx_sflash *sflash, struct bcma_drv_cc *bcc); ++void bcm47xx_sflash_struct_ssb_init(struct bcm47xx_sflash *sflash, struct ssb_chipcommon *scc); ++ ++extern struct bcm47xx_sflash bcm47xx_sflash; diff --git a/target/linux/brcm47xx/patches-3.3/025-mtd-bcm47xx-add-bcm47xx-part-parser.patch b/target/linux/brcm47xx/patches-3.3/025-mtd-bcm47xx-add-bcm47xx-part-parser.patch new file mode 100644 index 0000000000..5381e2f6cc --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/025-mtd-bcm47xx-add-bcm47xx-part-parser.patch @@ -0,0 +1,571 @@ +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -172,6 +172,13 @@ config MTD_MYLOADER_PARTS + You will still need the parsing functions to be called by the driver + for your particular device. It won't happen automatically. + ++config MTD_BCM47XX_PARTS ++ tristate "BCM47XX partitioning support" ++ default y ++ depends on BCM47XX ++ ---help--- ++ bcm47XX partitioning support ++ + comment "User Modules And Translation Layers" + + config MTD_CHAR +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -13,6 +13,7 @@ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o + obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o + obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o + obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o ++obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o + + # 'Users' - code which presents functionality to userspace. + obj-$(CONFIG_MTD_CHAR) += mtdchar.o +--- /dev/null ++++ b/drivers/mtd/bcm47xxpart.c +@@ -0,0 +1,542 @@ ++/* ++ * Copyright (C) 2006 Felix Fietkau ++ * Copyright (C) 2005 Waldemar Brodkorb ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define TRX_MAGIC 0x30524448 /* "HDR0" */ ++#define TRX_VERSION 1 ++#define TRX_MAX_LEN 0x3A0000 ++#define TRX_NO_HEADER 1 /* Do not write TRX header */ ++#define TRX_GZ_FILES 0x2 /* Contains up to TRX_MAX_OFFSET individual gzip files */ ++#define TRX_MAX_OFFSET 3 ++ ++struct trx_header { ++ u32 magic; /* "HDR0" */ ++ u32 len; /* Length of file including header */ ++ u32 crc32; /* 32-bit CRC from flag_version to end of file */ ++ u32 flag_version; /* 0:15 flags, 16:31 version */ ++ u32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of header */ ++}; ++ ++/* for Edimax Print servers which use an additional header ++ * then the firmware on flash looks like : ++ * EDIMAX HEADER | TRX HEADER ++ * As this header is 12 bytes long we have to handle it ++ * and skip it to find the TRX header ++ */ ++#define EDIMAX_PS_HEADER_MAGIC 0x36315350 /* "PS16" */ ++#define EDIMAX_PS_HEADER_LEN 0xc /* 12 bytes long for edimax header */ ++ ++#define NVRAM_SPACE 0x8000 ++ ++#define ROUTER_NETGEAR_WGR614L 1 ++#define ROUTER_NETGEAR_WNR834B 2 ++#define ROUTER_NETGEAR_WNDR3300 3 ++#define ROUTER_NETGEAR_WNR3500L 4 ++#define ROUTER_SIMPLETECH_SIMPLESHARE 5 ++#define ROUTER_NETGEAR_WNDR3400 6 ++ ++static int ++find_cfe_size(struct mtd_info *mtd) ++{ ++ struct trx_header *trx; ++ unsigned char buf[512]; ++ int off; ++ size_t len; ++ int blocksize; ++ ++ trx = (struct trx_header *) buf; ++ ++ blocksize = mtd->erasesize; ++ if (blocksize < 0x10000) ++ blocksize = 0x10000; ++ ++ for (off = (128*1024); off < mtd->size; off += blocksize) { ++ memset(buf, 0xe5, sizeof(buf)); ++ ++ /* ++ * Read into buffer ++ */ ++ if (mtd->read(mtd, off, sizeof(buf), &len, buf) || ++ len != sizeof(buf)) ++ continue; ++ ++ if (le32_to_cpu(trx->magic) == EDIMAX_PS_HEADER_MAGIC) { ++ if (mtd->read(mtd, off + EDIMAX_PS_HEADER_LEN, ++ sizeof(buf), &len, buf) || len != sizeof(buf)) { ++ continue; ++ } else { ++ pr_notice("Found edimax header\n"); ++ } ++ } ++ ++ /* found a TRX header */ ++ if (le32_to_cpu(trx->magic) == TRX_MAGIC) ++ goto found; ++ } ++ ++ pr_notice("%s: Couldn't find bootloader size\n", mtd->name); ++ return -1; ++ ++ found: ++ pr_notice("bootloader size: %d\n", off); ++ return off; ++ ++} ++ ++/* ++ * Copied from mtdblock.c ++ * ++ * Cache stuff... ++ * ++ * Since typical flash erasable sectors are much larger than what Linux's ++ * buffer cache can handle, we must implement read-modify-write on flash ++ * sectors for each block write requests. To avoid over-erasing flash sectors ++ * and to speed things up, we locally cache a whole flash sector while it is ++ * being written to until a different sector is required. ++ */ ++ ++static void erase_callback(struct erase_info *done) ++{ ++ wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; ++ wake_up(wait_q); ++} ++ ++static int erase_write(struct mtd_info *mtd, unsigned long pos, ++ int len, const char *buf) ++{ ++ struct erase_info erase; ++ DECLARE_WAITQUEUE(wait, current); ++ wait_queue_head_t wait_q; ++ size_t retlen; ++ int ret; ++ ++ /* ++ * First, let's erase the flash block. ++ */ ++ ++ init_waitqueue_head(&wait_q); ++ erase.mtd = mtd; ++ erase.callback = erase_callback; ++ erase.addr = pos; ++ erase.len = len; ++ erase.priv = (u_long)&wait_q; ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ add_wait_queue(&wait_q, &wait); ++ ++ ret = mtd->erase(mtd, &erase); ++ if (ret) { ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&wait_q, &wait); ++ pr_warn("erase of region [0x%lx, 0x%x] on \"%s\" failed\n", ++ pos, len, mtd->name); ++ return ret; ++ } ++ ++ schedule(); /* Wait for erase to finish. */ ++ remove_wait_queue(&wait_q, &wait); ++ ++ /* ++ * Next, write data to flash. ++ */ ++ ++ ret = mtd->write(mtd, pos, len, &retlen, buf); ++ if (ret) ++ return ret; ++ if (retlen != len) ++ return -EIO; ++ return 0; ++} ++ ++ ++static int ++find_dual_image_off(struct mtd_info *mtd) ++{ ++ struct trx_header trx; ++ int off, blocksize; ++ size_t len; ++ ++ blocksize = mtd->erasesize; ++ if (blocksize < 0x10000) ++ blocksize = 0x10000; ++ ++ for (off = (128*1024); off < mtd->size; off += blocksize) { ++ memset(&trx, 0xe5, sizeof(trx)); ++ /* ++ * Read into buffer ++ */ ++ if (mtd->read(mtd, off, sizeof(trx), &len, (char *) &trx) || ++ len != sizeof(trx)) ++ continue; ++ /* found last TRX header */ ++ if (le32_to_cpu(trx.magic) == TRX_MAGIC) { ++ if (le32_to_cpu(trx.flag_version >> 16) == 2) { ++ pr_notice("dual image TRX header found\n"); ++ return mtd->size / 2; ++ } else { ++ return 0; ++ } ++ } ++ } ++ return 0; ++} ++ ++ ++static int ++find_root(struct mtd_info *mtd, struct mtd_partition *part) ++{ ++ struct trx_header trx, *trx2; ++ unsigned char buf[512], *block; ++ int off, blocksize, trxoff = 0; ++ u32 i, crc = ~0; ++ size_t len; ++ bool edimax = false; ++ ++ blocksize = mtd->erasesize; ++ if (blocksize < 0x10000) ++ blocksize = 0x10000; ++ ++ for (off = (128*1024); off < mtd->size; off += blocksize) { ++ memset(&trx, 0xe5, sizeof(trx)); ++ ++ /* ++ * Read into buffer ++ */ ++ if (mtd->read(mtd, off, sizeof(trx), &len, (char *) &trx) || ++ len != sizeof(trx)) ++ continue; ++ ++ /* found an edimax header */ ++ if (le32_to_cpu(trx.magic) == EDIMAX_PS_HEADER_MAGIC) { ++ /* read the correct trx header */ ++ if (mtd->read(mtd, off + EDIMAX_PS_HEADER_LEN, ++ sizeof(trx), &len, (char *) &trx) || ++ len != sizeof(trx)) { ++ continue; ++ } else { ++ pr_notice("Found an edimax ps header\n"); ++ edimax = true; ++ } ++ } ++ ++ /* found a TRX header */ ++ if (le32_to_cpu(trx.magic) == TRX_MAGIC) { ++ part->offset = le32_to_cpu(trx.offsets[2]) ? : ++ le32_to_cpu(trx.offsets[1]); ++ part->size = le32_to_cpu(trx.len); ++ ++ part->size -= part->offset; ++ part->offset += off; ++ if (edimax) { ++ off += EDIMAX_PS_HEADER_LEN; ++ trxoff = EDIMAX_PS_HEADER_LEN; ++ } ++ ++ goto found; ++ } ++ } ++ ++ pr_warn("%s: Couldn't find root filesystem\n", ++ mtd->name); ++ return -1; ++ ++ found: ++ pr_notice("TRX offset : %x\n", trxoff); ++ if (part->size == 0) ++ return 0; ++ ++ if (mtd->read(mtd, part->offset, sizeof(buf), &len, buf) || len != sizeof(buf)) ++ return 0; ++ ++ /* Move the fs outside of the trx */ ++ part->size = 0; ++ ++ if (trx.len != part->offset + part->size - off) { ++ /* Update the trx offsets and length */ ++ trx.len = part->offset + part->size - off; ++ ++ /* Update the trx crc32 */ ++ for (i = (u32) &(((struct trx_header *)NULL)->flag_version); i <= trx.len; i += sizeof(buf)) { ++ if (mtd->read(mtd, off + i, sizeof(buf), &len, buf) || len != sizeof(buf)) ++ return 0; ++ crc = crc32_le(crc, buf, min(sizeof(buf), trx.len - i)); ++ } ++ trx.crc32 = crc; ++ ++ /* read first eraseblock from the trx */ ++ block = kmalloc(mtd->erasesize, GFP_KERNEL); ++ trx2 = (struct trx_header *) block; ++ if (mtd->read(mtd, off - trxoff, mtd->erasesize, &len, block) || len != mtd->erasesize) { ++ pr_err("Error accessing the first trx eraseblock\n"); ++ return 0; ++ } ++ ++ pr_notice("Updating TRX offsets and length:\n"); ++ pr_notice("old trx = [0x%08x, 0x%08x, 0x%08x], len=0x%08x crc32=0x%08x\n", trx2->offsets[0], trx2->offsets[1], trx2->offsets[2], trx2->len, trx2->crc32); ++ pr_notice("new trx = [0x%08x, 0x%08x, 0x%08x], len=0x%08x crc32=0x%08x\n", trx.offsets[0], trx.offsets[1], trx.offsets[2], trx.len, trx.crc32); ++ ++ /* Write updated trx header to the flash */ ++ memcpy(block + trxoff, &trx, sizeof(trx)); ++ if (mtd->unlock) ++ mtd->unlock(mtd, off - trxoff, mtd->erasesize); ++ erase_write(mtd, off - trxoff, mtd->erasesize, block); ++ if (mtd->sync) ++ mtd->sync(mtd); ++ kfree(block); ++ pr_notice("Done\n"); ++ } ++ ++ return part->size; ++} ++ ++static int get_router(void) ++{ ++ char buf[20]; ++ u32 boardnum = 0; ++ u16 boardtype = 0; ++ u16 boardrev = 0; ++ u32 boardflags = 0; ++ u16 sdram_init = 0; ++ u16 cardbus = 0; ++ u16 strev = 0; ++ ++ if (nvram_getenv("boardnum", buf, sizeof(buf)) >= 0) ++ boardnum = simple_strtoul(buf, NULL, 0); ++ if (nvram_getenv("boardtype", buf, sizeof(buf)) >= 0) ++ boardtype = simple_strtoul(buf, NULL, 0); ++ if (nvram_getenv("boardrev", buf, sizeof(buf)) >= 0) ++ boardrev = simple_strtoul(buf, NULL, 0); ++ if (nvram_getenv("boardflags", buf, sizeof(buf)) >= 0) ++ boardflags = simple_strtoul(buf, NULL, 0); ++ if (nvram_getenv("sdram_init", buf, sizeof(buf)) >= 0) ++ sdram_init = simple_strtoul(buf, NULL, 0); ++ if (nvram_getenv("cardbus", buf, sizeof(buf)) >= 0) ++ cardbus = simple_strtoul(buf, NULL, 0); ++ if (nvram_getenv("st_rev", buf, sizeof(buf)) >= 0) ++ strev = simple_strtoul(buf, NULL, 0); ++ ++ if ((boardnum == 8 || boardnum == 01) ++ && boardtype == 0x0472 && cardbus == 1) { ++ /* Netgear WNR834B, Netgear WNR834Bv2 */ ++ return ROUTER_NETGEAR_WNR834B; ++ } ++ ++ if (boardnum == 01 && boardtype == 0x0472 && boardrev == 0x23) { ++ /* Netgear WNDR-3300 */ ++ return ROUTER_NETGEAR_WNDR3300; ++ } ++ ++ if ((boardnum == 83258 || boardnum == 01) ++ && boardtype == 0x048e ++ && (boardrev == 0x11 || boardrev == 0x10) ++ && boardflags == 0x750 ++ && sdram_init == 0x000A) { ++ /* Netgear WGR614v8/L/WW 16MB ram, cfe v1.3 or v1.5 */ ++ return ROUTER_NETGEAR_WGR614L; ++ } ++ ++ if ((boardnum == 1 || boardnum == 3500) ++ && boardtype == 0x04CF ++ && (boardrev == 0x1213 || boardrev == 02)) { ++ /* Netgear WNR3500v2/U/L */ ++ return ROUTER_NETGEAR_WNR3500L; ++ } ++ ++ if (boardnum == 1 && boardtype == 0xb4cf && boardrev == 0x1100) { ++ /* Netgear WNDR3400 */ ++ return ROUTER_NETGEAR_WNDR3400; ++ } ++ ++ if (boardtype == 0x042f ++ && boardrev == 0x10 ++ && boardflags == 0 ++ && strev == 0x11) { ++ /* Simpletech Simpleshare */ ++ return ROUTER_SIMPLETECH_SIMPLESHARE; ++ } ++ ++ return 0; ++} ++ ++static int parse_bcm47xx_partitions(struct mtd_info *mtd, ++ struct mtd_partition **pparts, ++ struct mtd_part_parser_data *data) ++{ ++ int cfe_size; ++ int dual_image_offset = 0; ++ /* e.g Netgear 0x003e0000-0x003f0000 : "board_data", we exclude this ++ * part from our mapping to prevent overwriting len/checksum on e.g. ++ * Netgear WGR614v8/L/WW ++ */ ++ int custom_data_size = 0; ++ struct mtd_partition *bcm47xx_parts; ++ ++ cfe_size = find_cfe_size(mtd); ++ if (cfe_size < 0) ++ return 0; ++ ++ bcm47xx_parts = kzalloc(sizeof(struct mtd_partition) * 6, GFP_KERNEL); ++ ++ bcm47xx_parts[0].name = "cfe"; ++ bcm47xx_parts[1].name = "linux"; ++ bcm47xx_parts[2].name = "rootfs"; ++ bcm47xx_parts[3].name = "nvram"; ++ ++ /* boot loader */ ++ bcm47xx_parts[0].mask_flags = MTD_WRITEABLE; ++ bcm47xx_parts[0].offset = 0; ++ bcm47xx_parts[0].size = cfe_size; ++ ++ /* nvram */ ++ if (cfe_size != 384 * 1024) { ++ ++ switch (get_router()) { ++ case ROUTER_NETGEAR_WGR614L: ++ case ROUTER_NETGEAR_WNR834B: ++ case ROUTER_NETGEAR_WNDR3300: ++ case ROUTER_NETGEAR_WNR3500L: ++ case ROUTER_NETGEAR_WNDR3400: ++ /* Netgear: checksum is @ 0x003AFFF8 for 4M flash or checksum ++ * is @ 0x007AFFF8 for 8M flash ++ */ ++ custom_data_size = mtd->erasesize; ++ ++ bcm47xx_parts[3].offset = mtd->size - roundup(NVRAM_SPACE, mtd->erasesize); ++ bcm47xx_parts[3].size = roundup(NVRAM_SPACE, mtd->erasesize); ++ ++ /* Place CFE board_data into a partition */ ++ bcm47xx_parts[4].name = "board_data"; ++ bcm47xx_parts[4].offset = bcm47xx_parts[3].offset - custom_data_size; ++ bcm47xx_parts[4].size = custom_data_size; ++ break; ++ ++ case ROUTER_SIMPLETECH_SIMPLESHARE: ++ /* Fixup Simpletech Simple share nvram */ ++ ++ pr_notice("Setting up simpletech nvram\n"); ++ custom_data_size = mtd->erasesize; ++ ++ bcm47xx_parts[3].offset = mtd->size - roundup(NVRAM_SPACE, mtd->erasesize) * 2; ++ bcm47xx_parts[3].size = roundup(NVRAM_SPACE, mtd->erasesize); ++ ++ /* Place backup nvram into a partition */ ++ bcm47xx_parts[4].name = "nvram_copy"; ++ bcm47xx_parts[4].offset = mtd->size - roundup(NVRAM_SPACE, mtd->erasesize); ++ bcm47xx_parts[4].size = roundup(NVRAM_SPACE, mtd->erasesize); ++ break; ++ ++ default: ++ bcm47xx_parts[3].offset = mtd->size - roundup(NVRAM_SPACE, mtd->erasesize); ++ bcm47xx_parts[3].size = roundup(NVRAM_SPACE, mtd->erasesize); ++ } ++ ++ } else { ++ /* nvram (old 128kb config partition on netgear wgt634u) */ ++ bcm47xx_parts[3].offset = bcm47xx_parts[0].size; ++ bcm47xx_parts[3].size = roundup(NVRAM_SPACE, mtd->erasesize); ++ } ++ ++ /* dual image offset*/ ++ pr_notice("Looking for dual image\n"); ++ dual_image_offset = find_dual_image_off(mtd); ++ /* linux (kernel and rootfs) */ ++ if (cfe_size != 384 * 1024) { ++ if (get_router() == ROUTER_SIMPLETECH_SIMPLESHARE) { ++ bcm47xx_parts[1].offset = bcm47xx_parts[0].size; ++ bcm47xx_parts[1].size = bcm47xx_parts[4].offset - dual_image_offset - ++ bcm47xx_parts[1].offset - custom_data_size; ++ } else { ++ bcm47xx_parts[1].offset = bcm47xx_parts[0].size; ++ bcm47xx_parts[1].size = bcm47xx_parts[3].offset - dual_image_offset - ++ bcm47xx_parts[1].offset - custom_data_size; ++ } ++ } else { ++ /* do not count the elf loader, which is on one block */ ++ bcm47xx_parts[1].offset = bcm47xx_parts[0].size + ++ bcm47xx_parts[3].size + mtd->erasesize; ++ bcm47xx_parts[1].size = mtd->size - ++ bcm47xx_parts[0].size - ++ (2*bcm47xx_parts[3].size) - ++ mtd->erasesize - custom_data_size; ++ } ++ ++ /* find and size rootfs */ ++ find_root(mtd, &bcm47xx_parts[2]); ++ bcm47xx_parts[2].size = mtd->size - dual_image_offset - ++ bcm47xx_parts[2].offset - ++ bcm47xx_parts[3].size - custom_data_size; ++ *pparts = bcm47xx_parts; ++ return bcm47xx_parts[4].name == NULL ? 4 : 5; ++} ++ ++static struct mtd_part_parser bcm47xx_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = parse_bcm47xx_partitions, ++ .name = "bcm47xx", ++}; ++ ++static int __init bcm47xx_parser_init(void) ++{ ++ return register_mtd_parser(&bcm47xx_parser); ++} ++ ++static void __exit bcm47xx_parser_exit(void) ++{ ++ deregister_mtd_parser(&bcm47xx_parser); ++} ++ ++module_init(bcm47xx_parser_init); ++module_exit(bcm47xx_parser_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Parsing code for flash partitions on bcm47xx SoCs"); diff --git a/target/linux/brcm47xx/patches-3.3/026-mtd-bcm47xx-add-parallel-flash-driver.patch b/target/linux/brcm47xx/patches-3.3/026-mtd-bcm47xx-add-parallel-flash-driver.patch new file mode 100644 index 0000000000..7f8f64f28c --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/026-mtd-bcm47xx-add-parallel-flash-driver.patch @@ -0,0 +1,216 @@ +--- a/drivers/mtd/maps/Kconfig ++++ b/drivers/mtd/maps/Kconfig +@@ -248,6 +248,15 @@ config MTD_LANTIQ + help + Support for NOR flash attached to the Lantiq SoC's External Bus Unit. + ++config MTD_BCM47XX_PFLASH ++ tristate "bcm47xx parallel flash support" ++ default y ++ depends on BCM47XX ++ select MTD_PARTITIONS ++ select MTD_BCM47XX_PARTS ++ help ++ Support for bcm47xx parallel flash ++ + config MTD_DILNETPC + tristate "CFI Flash device mapped on DIL/Net PC" + depends on X86 && MTD_CFI_INTELEXT && BROKEN +--- a/drivers/mtd/maps/Makefile ++++ b/drivers/mtd/maps/Makefile +@@ -57,3 +57,4 @@ obj-$(CONFIG_MTD_VMU) += vmu-flash.o + obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o + obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o + obj-$(CONFIG_MTD_LANTIQ) += lantiq-flash.o ++obj-$(CONFIG_MTD_BCM47XX_PFLASH)+= bcm47xx-pflash.o +--- /dev/null ++++ b/drivers/mtd/maps/bcm47xx-pflash.c +@@ -0,0 +1,188 @@ ++/* ++ * Copyright (C) 2006 Felix Fietkau ++ * Copyright (C) 2005 Waldemar Brodkorb ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define WINDOW_ADDR 0x1fc00000 ++#define WINDOW_SIZE 0x400000 ++#define BUSWIDTH 2 ++ ++static struct mtd_info *bcm47xx_mtd; ++ ++static void bcm47xx_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) ++{ ++ if (len == 1) { ++ memcpy_fromio(to, map->virt + from, len); ++ } else { ++ int i; ++ u16 *dest = (u16 *) to; ++ u16 *src = (u16 *) (map->virt + from); ++ for (i = 0; i < (len / 2); i++) ++ dest[i] = src[i]; ++ if (len & 1) ++ *((u8 *)dest+len-1) = src[i] & 0xff; ++ } ++} ++ ++static struct map_info bcm47xx_map = { ++ name: "Physically mapped flash", ++ size : WINDOW_SIZE, ++ bankwidth : BUSWIDTH, ++ phys : WINDOW_ADDR, ++}; ++ ++static const char *probes[] = { "bcm47xx", NULL }; ++ ++static int bcm47xx_mtd_probe(struct platform_device *pdev) ++{ ++#ifdef CONFIG_BCM47XX_SSB ++ struct ssb_chipcommon *ssb_cc; ++#endif ++#ifdef CONFIG_BCM47XX_BCMA ++ struct bcma_drv_cc *bcma_cc; ++#endif ++ int ret = 0; ++ ++ switch (bcm47xx_bus_type) { ++#ifdef CONFIG_BCM47XX_SSB ++ case BCM47XX_BUS_TYPE_SSB: ++ ssb_cc = &bcm47xx_bus.ssb.chipco; ++ if (ssb_cc->flash_type != SSB_PFLASH) ++ return -ENODEV; ++ ++ bcm47xx_map.phys = ssb_cc->pflash.window; ++ bcm47xx_map.size = ssb_cc->pflash.window_size; ++ bcm47xx_map.bankwidth = ssb_cc->pflash.buswidth; ++ break; ++#endif ++#ifdef CONFIG_BCM47XX_BCMA ++ case BCM47XX_BUS_TYPE_BCMA: ++ bcma_cc = &bcm47xx_bus.bcma.bus.drv_cc; ++ if (bcma_cc->flash_type != BCMA_PFLASH) ++ return -ENODEV; ++ ++ bcm47xx_map.phys = bcma_cc->pflash.window; ++ bcm47xx_map.size = bcma_cc->pflash.window_size; ++ bcm47xx_map.bankwidth = bcma_cc->pflash.buswidth; ++ break; ++#endif ++ } ++ ++ pr_notice("flash init: 0x%08x 0x%08lx\n", bcm47xx_map.phys, bcm47xx_map.size); ++ bcm47xx_map.virt = ioremap_nocache(bcm47xx_map.phys, bcm47xx_map.size); ++ ++ if (!bcm47xx_map.virt) { ++ pr_err("Failed to ioremap\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&bcm47xx_map); ++ /* override copy_from routine */ ++ bcm47xx_map.copy_from = bcm47xx_map_copy_from; ++ ++ bcm47xx_mtd = do_map_probe("cfi_probe", &bcm47xx_map); ++ if (!bcm47xx_mtd) { ++ pr_err("Failed to do_map_probe\n"); ++ ret = -ENXIO; ++ goto err_unmap; ++ } ++ bcm47xx_mtd->owner = THIS_MODULE; ++ ++ pr_notice("Flash device: 0x%lx at 0x%x\n", bcm47xx_map.size, WINDOW_ADDR); ++ ++ ret = mtd_device_parse_register(bcm47xx_mtd, probes, NULL, NULL, 0); ++ ++ if (ret) { ++ pr_err("Flash: mtd_device_register failed\n"); ++ goto err_destroy; ++ } ++ return 0; ++ ++err_destroy: ++ map_destroy(bcm47xx_mtd); ++err_unmap: ++ iounmap(bcm47xx_map.virt); ++ return ret; ++} ++ ++static int __devexit bcm47xx_mtd_remove(struct platform_device *pdev) ++{ ++ mtd_device_unregister(bcm47xx_mtd); ++ map_destroy(bcm47xx_mtd); ++ iounmap(bcm47xx_map.virt); ++ return 0; ++} ++ ++static struct platform_driver bcm47xx_mtd_driver = { ++ .remove = __devexit_p(bcm47xx_mtd_remove), ++ .driver = { ++ .name = "bcm47xx_pflash", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init init_bcm47xx_mtd(void) ++{ ++ int ret = platform_driver_probe(&bcm47xx_mtd_driver, bcm47xx_mtd_probe); ++ ++ if (ret) ++ pr_err("error registering platform driver: %i\n", ret); ++ return ret; ++} ++ ++static void __exit exit_bcm47xx_mtd(void) ++{ ++ platform_driver_unregister(&bcm47xx_mtd_driver); ++} ++ ++module_init(init_bcm47xx_mtd); ++module_exit(exit_bcm47xx_mtd); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("BCM47XX parallel flash driver"); diff --git a/target/linux/brcm47xx/patches-3.3/027-mtd-bcm47xx-add-serial-flash-driver.patch b/target/linux/brcm47xx/patches-3.3/027-mtd-bcm47xx-add-serial-flash-driver.patch new file mode 100644 index 0000000000..1e2577b728 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/027-mtd-bcm47xx-add-serial-flash-driver.patch @@ -0,0 +1,310 @@ +--- a/arch/mips/include/asm/mach-bcm47xx/bus.h ++++ b/arch/mips/include/asm/mach-bcm47xx/bus.h +@@ -11,6 +11,7 @@ + + #include + #include ++#include + #include + + struct bcm47xx_sflash { +@@ -28,6 +29,8 @@ struct bcm47xx_sflash { + u32 blocksize; /* Block size */ + u32 numblocks; /* Number of blocks */ + u32 size; /* Total size in bytes */ ++ ++ struct mtd_info *mtd; + }; + + void bcm47xx_sflash_struct_bcma_init(struct bcm47xx_sflash *sflash, struct bcma_drv_cc *bcc); +--- a/drivers/mtd/maps/Kconfig ++++ b/drivers/mtd/maps/Kconfig +@@ -257,6 +257,15 @@ config MTD_BCM47XX_PFLASH + help + Support for bcm47xx parallel flash + ++config MTD_BCM47XX_SFLASH ++ tristate "bcm47xx serial flash support" ++ default y ++ depends on BCM47XX ++ select MTD_PARTITIONS ++ select MTD_BCM47XX_PARTS ++ help ++ Support for bcm47xx parallel flash ++ + config MTD_DILNETPC + tristate "CFI Flash device mapped on DIL/Net PC" + depends on X86 && MTD_CFI_INTELEXT && BROKEN +--- a/drivers/mtd/maps/Makefile ++++ b/drivers/mtd/maps/Makefile +@@ -58,3 +58,4 @@ obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr + obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o + obj-$(CONFIG_MTD_LANTIQ) += lantiq-flash.o + obj-$(CONFIG_MTD_BCM47XX_PFLASH)+= bcm47xx-pflash.o ++obj-$(CONFIG_MTD_BCM47XX_SFLASH)+= bcm47xx-sflash.o +--- /dev/null ++++ b/drivers/mtd/maps/bcm47xx-sflash.c +@@ -0,0 +1,263 @@ ++/* ++ * Broadcom SiliconBackplane chipcommon serial flash interface ++ * ++ * Copyright 2011, 2012, Hauke Mehrtens ++ * Copyright 2006, Broadcom Corporation ++ * All Rights Reserved. ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++ ++#define pr_fmt(fmt) "bcm47xx_sflash: " fmt ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int ++sflash_mtd_poll(struct bcm47xx_sflash *sflash, unsigned int offset, int timeout) ++{ ++ unsigned long now = jiffies; ++ ++ for (;;) { ++ if (!sflash->poll(sflash, offset)) { ++ break; ++ } ++ if (time_after(jiffies, now + timeout)) { ++ pr_err("timeout while polling\n"); ++ return -ETIMEDOUT; ++ ++ } ++ cpu_relax(); ++ udelay(1); ++ } ++ ++ return 0; ++} ++ ++static int ++sflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) ++{ ++ struct bcm47xx_sflash *sflash = (struct bcm47xx_sflash *)mtd->priv; ++ ++ /* Check address range */ ++ if (!len) ++ return 0; ++ ++ if ((from + len) > mtd->size) ++ return -EINVAL; ++ ++ *retlen = 0; ++ while (len) { ++ int ret = sflash->read(sflash, from, len, buf); ++ if (ret < 0) ++ return ret; ++ ++ from += (loff_t) ret; ++ len -= ret; ++ buf += ret; ++ *retlen += ret; ++ } ++ ++ return 0; ++} ++ ++static int ++sflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) ++{ ++ int bytes; ++ int ret; ++ struct bcm47xx_sflash *sflash = (struct bcm47xx_sflash *)mtd->priv; ++ ++ /* Check address range */ ++ if (!len) ++ return 0; ++ ++ if ((to + len) > mtd->size) ++ return -EINVAL; ++ ++ *retlen = 0; ++ while (len) { ++ ret = sflash->write(sflash, to, len, buf); ++ if (ret < 0) ++ return ret; ++ ++ bytes = ret; ++ ++ ret = sflash_mtd_poll(sflash, (unsigned int) to, HZ / 10); ++ if (ret) ++ return ret; ++ ++ to += (loff_t) bytes; ++ len -= bytes; ++ buf += bytes; ++ *retlen += bytes; ++ } ++ ++ return 0; ++} ++ ++static int ++sflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) ++{ ++ struct bcm47xx_sflash *sflash = (struct bcm47xx_sflash *) mtd->priv; ++ int i, j, ret = 0; ++ unsigned int addr, len; ++ ++ /* Check address range */ ++ if (!erase->len) ++ return 0; ++ if ((erase->addr + erase->len) > mtd->size) ++ return -EINVAL; ++ ++ addr = erase->addr; ++ len = erase->len; ++ ++ /* Ensure that requested regions are aligned */ ++ for (i = 0; i < mtd->numeraseregions; i++) { ++ for (j = 0; j < mtd->eraseregions[i].numblocks; j++) { ++ if (addr == mtd->eraseregions[i].offset + ++ mtd->eraseregions[i].erasesize * j && ++ len >= mtd->eraseregions[i].erasesize) { ++ ret = sflash->erase(sflash, addr); ++ if (ret < 0) ++ break; ++ ret = sflash_mtd_poll(sflash, addr, 10 * HZ); ++ if (ret) ++ break; ++ addr += mtd->eraseregions[i].erasesize; ++ len -= mtd->eraseregions[i].erasesize; ++ } ++ } ++ if (ret) ++ break; ++ } ++ ++ /* Set erase status */ ++ if (ret) ++ erase->state = MTD_ERASE_FAILED; ++ else ++ erase->state = MTD_ERASE_DONE; ++ ++ /* Call erase callback */ ++ if (erase->callback) ++ erase->callback(erase); ++ ++ return ret; ++} ++ ++static const char *probes[] = { "bcm47xx", NULL }; ++ ++static int bcm47xx_sflash_probe(struct platform_device *pdev) ++{ ++ struct bcm47xx_sflash *sflash = dev_get_platdata(&pdev->dev); ++ struct mtd_info *mtd; ++ struct mtd_erase_region_info *eraseregions; ++ int ret = 0; ++ ++ mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); ++ if (!mtd){ ++ ret = -ENOMEM; ++ goto err_out; ++ } ++ ++ eraseregions = kzalloc(sizeof(struct mtd_erase_region_info), GFP_KERNEL); ++ if (!eraseregions) { ++ ret = -ENOMEM; ++ goto err_free_mtd; ++ } ++ ++ pr_info("found serial flash: blocksize=%dKB, numblocks=%d, size=%dKB\n", ++ sflash->blocksize / 1024, sflash->numblocks, sflash->size / 1024); ++ ++ /* Setup region info */ ++ eraseregions->offset = 0; ++ eraseregions->erasesize = sflash->blocksize; ++ eraseregions->numblocks = sflash->numblocks; ++ if (eraseregions->erasesize > mtd->erasesize) ++ mtd->erasesize = eraseregions->erasesize; ++ mtd->size = sflash->size; ++ mtd->numeraseregions = 1; ++ ++ /* Register with MTD */ ++ mtd->name = "bcm47xx-sflash"; ++ mtd->type = MTD_NORFLASH; ++ mtd->flags = MTD_CAP_NORFLASH; ++ mtd->eraseregions = eraseregions; ++ mtd->erase = sflash_mtd_erase; ++ mtd->read = sflash_mtd_read; ++ mtd->write = sflash_mtd_write; ++ mtd->writesize = 1; ++ mtd->priv = sflash; ++ ret = dev_set_drvdata(&pdev->dev, mtd); ++ mtd->owner = THIS_MODULE; ++ if (ret) { ++ pr_err("adding private data failed\n"); ++ goto err_free_eraseregions; ++ } ++ ++ ret = mtd_device_parse_register(mtd, probes, NULL, NULL, 0); ++ ++ if (ret) { ++ pr_err("mtd_device_register failed\n"); ++ goto err_free_eraseregions; ++ } ++ return 0; ++ ++err_free_eraseregions: ++ kfree(eraseregions); ++err_free_mtd: ++ kfree(mtd); ++err_out: ++ return ret; ++} ++ ++static int __devexit bcm47xx_sflash_remove(struct platform_device *pdev) ++{ ++ struct mtd_info *mtd = dev_get_drvdata(&pdev->dev); ++ ++ if (mtd) { ++ mtd_device_unregister(mtd); ++ map_destroy(mtd); ++ kfree(mtd->eraseregions); ++ kfree(mtd); ++ dev_set_drvdata(&pdev->dev, NULL); ++ } ++ return 0; ++} ++ ++static struct platform_driver bcm47xx_sflash_driver = { ++ .remove = __devexit_p(bcm47xx_sflash_remove), ++ .driver = { ++ .name = "bcm47xx_sflash", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init init_bcm47xx_sflash(void) ++{ ++ int ret = platform_driver_probe(&bcm47xx_sflash_driver, bcm47xx_sflash_probe); ++ ++ if (ret) ++ pr_err("error registering platform driver: %i\n", ret); ++ return ret; ++} ++ ++static void __exit exit_bcm47xx_sflash(void) ++{ ++ platform_driver_unregister(&bcm47xx_sflash_driver); ++} ++ ++module_init(init_bcm47xx_sflash); ++module_exit(exit_bcm47xx_sflash); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("BCM47XX parallel flash driver"); diff --git a/target/linux/brcm47xx/patches-3.3/028-bcm47xx-register-flash-drivers.patch b/target/linux/brcm47xx/patches-3.3/028-bcm47xx-register-flash-drivers.patch new file mode 100644 index 0000000000..5fc568fc04 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/028-bcm47xx-register-flash-drivers.patch @@ -0,0 +1,136 @@ +--- a/arch/mips/bcm47xx/Kconfig ++++ b/arch/mips/bcm47xx/Kconfig +@@ -9,6 +9,7 @@ config BCM47XX_SSB + select SSB_EMBEDDED + select SSB_B43_PCI_BRIDGE if PCI + select SSB_PCICORE_HOSTMODE if PCI ++ select SSB_SFLASH + default y + help + Add support for old Broadcom BCM47xx boards with Sonics Silicon Backplane support. +@@ -22,6 +23,7 @@ config BCM47XX_BCMA + select BCMA_HOST_SOC + select BCMA_DRIVER_MIPS + select BCMA_DRIVER_PCI_HOSTMODE if PCI ++ select BCMA_SFLASH + default y + help + Add support for new Broadcom BCM47xx boards with Broadcom specific Advanced Microcontroller Bus. +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -31,10 +31,12 @@ + #include + #include + #include ++#include + #include + #include + #include + #include ++#include + #include + + union bcm47xx_bus bcm47xx_bus; +@@ -45,6 +47,32 @@ EXPORT_SYMBOL(bcm47xx_bus_type); + + struct bcm47xx_sflash bcm47xx_sflash; + ++static struct resource bcm47xx_pflash_resource = { ++ .name = "bcm47xx_pflash", ++ .start = 0, ++ .end = 0, ++ .flags = 0, ++}; ++ ++static struct platform_device bcm47xx_pflash_dev = { ++ .name = "bcm47xx_pflash", ++ .resource = &bcm47xx_pflash_resource, ++ .num_resources = 1, ++}; ++ ++static struct resource bcm47xx_sflash_resource = { ++ .name = "bcm47xx_sflash", ++ .start = 0, ++ .end = 0, ++ .flags = 0, ++}; ++ ++static struct platform_device bcm47xx_sflash_dev = { ++ .name = "bcm47xx_sflash", ++ .resource = &bcm47xx_sflash_resource, ++ .num_resources = 1, ++}; ++ + static void bcm47xx_machine_restart(char *command) + { + printk(KERN_ALERT "Please stand by while rebooting the system...\n"); +@@ -310,6 +338,24 @@ static void __init bcm47xx_register_ssb( + } + } + } ++ ++static int __init bcm47xx_register_flash_ssb(void) ++{ ++ struct ssb_chipcommon *chipco = &bcm47xx_bus.ssb.chipco; ++ ++ switch (chipco->flash_type) { ++ case SSB_PFLASH: ++ bcm47xx_pflash_resource.start = chipco->pflash.window; ++ bcm47xx_pflash_resource.end = chipco->pflash.window + chipco->pflash.window_size; ++ return platform_device_register(&bcm47xx_pflash_dev); ++ case SSB_SFLASH: ++ bcm47xx_sflash_dev.dev.platform_data = &bcm47xx_sflash; ++ return platform_device_register(&bcm47xx_sflash_dev); ++ default: ++ printk(KERN_ERR "No flash device found\n"); ++ return -1; ++ } ++} + #endif + + #ifdef CONFIG_BCM47XX_BCMA +@@ -324,6 +370,24 @@ static void __init bcm47xx_register_bcma + if (bcm47xx_bus.bcma.bus.drv_cc.flash_type == BCMA_SFLASH) + bcm47xx_sflash_struct_bcma_init(&bcm47xx_sflash, &bcm47xx_bus.bcma.bus.drv_cc); + } ++ ++static int __init bcm47xx_register_flash_bcma(void) ++{ ++ struct bcma_drv_cc *drv_cc = &bcm47xx_bus.bcma.bus.drv_cc; ++ ++ switch (drv_cc->flash_type) { ++ case BCMA_PFLASH: ++ bcm47xx_pflash_resource.start = drv_cc->pflash.window; ++ bcm47xx_pflash_resource.end = drv_cc->pflash.window + drv_cc->pflash.window_size; ++ return platform_device_register(&bcm47xx_pflash_dev); ++ case BCMA_SFLASH: ++ bcm47xx_sflash_dev.dev.platform_data = &bcm47xx_sflash; ++ return platform_device_register(&bcm47xx_sflash_dev); ++ default: ++ printk(KERN_ERR "No flash device found\n"); ++ return -1; ++ } ++} + #endif + + void __init plat_mem_setup(void) +@@ -366,3 +430,19 @@ static int __init bcm47xx_register_bus_c + return 0; + } + device_initcall(bcm47xx_register_bus_complete); ++ ++static int __init bcm47xx_register_flash(void) ++{ ++ switch (bcm47xx_bus_type) { ++#ifdef CONFIG_BCM47XX_SSB ++ case BCM47XX_BUS_TYPE_SSB: ++ return bcm47xx_register_flash_ssb(); ++#endif ++#ifdef CONFIG_BCM47XX_BCMA ++ case BCM47XX_BUS_TYPE_BCMA: ++ return bcm47xx_register_flash_bcma(); ++#endif ++ } ++ return -1; ++} ++fs_initcall(bcm47xx_register_flash); diff --git a/target/linux/brcm47xx/patches-3.3/029-bcm47xx-read-nvram-from-sflash.patch b/target/linux/brcm47xx/patches-3.3/029-bcm47xx-read-nvram-from-sflash.patch new file mode 100644 index 0000000000..b3d13df39f --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/029-bcm47xx-read-nvram-from-sflash.patch @@ -0,0 +1,138 @@ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -20,11 +20,12 @@ + #include + #include + #include ++#include + + static char nvram_buf[NVRAM_SPACE]; + + /* Probe for NVRAM header */ +-static void early_nvram_init(void) ++static void early_nvram_init_pflash(void) + { + #ifdef CONFIG_BCM47XX_SSB + struct ssb_chipcommon *ssb_cc; +@@ -50,9 +51,6 @@ static void early_nvram_init(void) + #ifdef CONFIG_BCM47XX_BCMA + case BCM47XX_BUS_TYPE_BCMA: + bcma_cc = &bcm47xx_bus.bcma.bus.drv_cc; +- if (bcma_cc->flash_type != BCMA_PFLASH) +- return; +- + base = bcma_cc->pflash.window; + lim = bcma_cc->pflash.window_size; + break; +@@ -86,7 +84,110 @@ found: + for (i = 0; i < sizeof(struct nvram_header); i += 4) + *dst++ = *src++; + for (; i < header->len && i < NVRAM_SPACE; i += 4) +- *dst++ = le32_to_cpu(*src++); ++ *dst++ = *src++; ++} ++ ++static int early_nvram_init_sflash(void) ++{ ++ struct nvram_header header; ++ u32 off; ++ int ret; ++ char *dst; ++ int len; ++ ++ /* check if the struct is already initilized */ ++ if (!bcm47xx_sflash.size) ++ return -1; ++ ++ off = FLASH_MIN; ++ while (off <= bcm47xx_sflash.size) { ++ ret = bcm47xx_sflash.read(&bcm47xx_sflash, off - NVRAM_SPACE, sizeof(header), (u8 *)&header); ++ if (ret != sizeof(header)) ++ return ret; ++ if (header.magic == NVRAM_HEADER) ++ goto found; ++ off <<= 1; ++ } ++ ++ off = FLASH_MIN; ++ while (off <= bcm47xx_sflash.size) { ++ ret = bcm47xx_sflash.read(&bcm47xx_sflash, off - (2 * NVRAM_SPACE), sizeof(header), (u8 *)&header); ++ if (ret != sizeof(header)) ++ return ret; ++ if (header.magic == NVRAM_HEADER) ++ goto found; ++ off <<= 1; ++ } ++ return -1; ++ ++found: ++ len = NVRAM_SPACE; ++ dst = nvram_buf; ++ while (len) { ++ ret = bcm47xx_sflash.read(&bcm47xx_sflash, off - (2 * NVRAM_SPACE), len, dst); ++ if (ret < 0) ++ return ret; ++ off += ret; ++ len -= ret; ++ dst += ret; ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_BCM47XX_SSB ++static void early_nvram_init_ssb(void) ++{ ++ int err; ++ ++ switch (bcm47xx_bus.ssb.chipco.flash_type) { ++ case SSB_PFLASH: ++ early_nvram_init_pflash(); ++ break; ++ case SSB_SFLASH: ++ err = early_nvram_init_sflash(); ++ if (err < 0) ++ printk(KERN_WARNING "can not read from flash: %i\n", err); ++ break; ++ default: ++ printk(KERN_WARNING "unknow flash type\n"); ++ } ++} ++#endif ++ ++#ifdef CONFIG_BCM47XX_BCMA ++static void early_nvram_init_bcma(void) ++{ ++ int err; ++ ++ switch (bcm47xx_bus.bcma.bus.drv_cc.flash_type) { ++ case BCMA_PFLASH: ++ early_nvram_init_pflash(); ++ break; ++ case BCMA_SFLASH: ++ err = early_nvram_init_sflash(); ++ if (err < 0) ++ printk(KERN_WARNING "can not read from flash: %i\n", err); ++ break; ++ default: ++ printk(KERN_WARNING "unknow flash type\n"); ++ } ++} ++#endif ++ ++static void early_nvram_init(void) ++{ ++ switch (bcm47xx_bus_type) { ++#ifdef CONFIG_BCM47XX_SSB ++ case BCM47XX_BUS_TYPE_SSB: ++ early_nvram_init_ssb(); ++ break; ++#endif ++#ifdef CONFIG_BCM47XX_BCMA ++ case BCM47XX_BUS_TYPE_BCMA: ++ early_nvram_init_bcma(); ++ break; ++#endif ++ } + } + + int nvram_getenv(char *name, char *val, size_t val_len) diff --git a/target/linux/brcm47xx/patches-3.3/030-bcm47xx-bcma-nandflash.patch b/target/linux/brcm47xx/patches-3.3/030-bcm47xx-bcma-nandflash.patch new file mode 100644 index 0000000000..03dd321b16 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/030-bcm47xx-bcma-nandflash.patch @@ -0,0 +1,1157 @@ +--- a/arch/mips/bcm47xx/Kconfig ++++ b/arch/mips/bcm47xx/Kconfig +@@ -24,6 +24,7 @@ config BCM47XX_BCMA + select BCMA_DRIVER_MIPS + select BCMA_DRIVER_PCI_HOSTMODE if PCI + select BCMA_SFLASH ++ select BCMA_NFLASH + default y + help + Add support for new Broadcom BCM47xx boards with Broadcom specific Advanced Microcontroller Bus. +--- a/arch/mips/bcm47xx/bus.c ++++ b/arch/mips/bcm47xx/bus.c +@@ -2,6 +2,7 @@ + * BCM947xx nvram variable access + * + * Copyright (C) 2011 Hauke Mehrtens ++ * Copyright (C) 2011-2012 Tathagata Das + * + * 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 +@@ -80,3 +81,9 @@ void bcm47xx_sflash_struct_ssb_init(stru + sflash->numblocks = scc->sflash.numblocks; + sflash->size = scc->sflash.size; + } ++ ++void bcm47xx_nflash_struct_bcma_init(struct bcm47xx_nflash *nflash, struct bcma_drv_cc *bcc) ++{ ++ nflash->nflash_type = BCM47XX_BUS_TYPE_BCMA; ++ nflash->bcc = bcc; ++} +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -4,6 +4,7 @@ + * Copyright (C) 2005 Broadcom Corporation + * Copyright (C) 2006 Felix Fietkau + * Copyright (C) 2010-2011 Hauke Mehrtens ++ * Copyright (C) 2011-2012 Tathagata Das + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the +@@ -21,6 +22,7 @@ + #include + #include + #include ++#include + + static char nvram_buf[NVRAM_SPACE]; + +@@ -134,6 +136,51 @@ found: + return 0; + } + ++static int early_nvram_init_nflash(void) ++{ ++ struct nvram_header *header; ++ u32 off; ++ int ret; ++ int len; ++ u32 flash_size = bcm47xx_nflash.size; ++ u8 tmpbuf[NFL_SECTOR_SIZE]; ++ int i; ++ u32 *src, *dst; ++ ++ /* check if the struct is already initilized */ ++ if (!flash_size) ++ return -1; ++ ++ cfe_env = 0; ++ ++ off = FLASH_MIN; ++ while (off <= flash_size) { ++ ret = bcma_nflash_read(bcm47xx_nflash.bcc, off, NFL_SECTOR_SIZE, tmpbuf); ++ if (ret != NFL_SECTOR_SIZE) ++ goto done; ++ header = (struct nvram_header *)tmpbuf; ++ if (header->magic == NVRAM_HEADER) ++ goto found; ++ off <<= 1; ++ } ++ ++ ret = -1; ++ goto done; ++ ++found: ++ len = header->len; ++ header = (struct nvram_header *) KSEG1ADDR(NAND_FLASH1 + off); ++ src = (u32 *) header; ++ dst = (u32 *) nvram_buf; ++ for (i = 0; i < sizeof(struct nvram_header); i += 4) ++ *dst++ = *src++; ++ for (; i < len && i < NVRAM_SPACE; i += 4) ++ *dst++ = *src++; ++ ret = 0; ++done: ++ return ret; ++} ++ + #ifdef CONFIG_BCM47XX_SSB + static void early_nvram_init_ssb(void) + { +@@ -168,6 +215,11 @@ static void early_nvram_init_bcma(void) + if (err < 0) + printk(KERN_WARNING "can not read from flash: %i\n", err); + break; ++ case BCMA_NFLASH: ++ err = early_nvram_init_nflash(); ++ if (err < 0) ++ printk(KERN_WARNING "can not read from nflash: %i\n", err); ++ break; + default: + printk(KERN_WARNING "unknow flash type\n"); + } +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -4,6 +4,7 @@ + * Copyright (C) 2006 Michael Buesch + * Copyright (C) 2010 Waldemar Brodkorb + * Copyright (C) 2010-2011 Hauke Mehrtens ++ * Copyright (C) 2011-2012 Tathagata Das + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the +@@ -46,6 +47,7 @@ enum bcm47xx_bus_type bcm47xx_bus_type; + EXPORT_SYMBOL(bcm47xx_bus_type); + + struct bcm47xx_sflash bcm47xx_sflash; ++struct bcm47xx_nflash bcm47xx_nflash; + + static struct resource bcm47xx_pflash_resource = { + .name = "bcm47xx_pflash", +@@ -73,6 +75,19 @@ static struct platform_device bcm47xx_sf + .num_resources = 1, + }; + ++static struct resource bcm47xx_nflash_resource = { ++ .name = "bcm47xx_nflash", ++ .start = 0, ++ .end = 0, ++ .flags = 0, ++}; ++ ++static struct platform_device bcm47xx_nflash_dev = { ++ .name = "bcm47xx_nflash", ++ .resource = &bcm47xx_nflash_resource, ++ .num_resources = 1, ++}; ++ + static void bcm47xx_machine_restart(char *command) + { + printk(KERN_ALERT "Please stand by while rebooting the system...\n"); +@@ -369,6 +384,9 @@ static void __init bcm47xx_register_bcma + + if (bcm47xx_bus.bcma.bus.drv_cc.flash_type == BCMA_SFLASH) + bcm47xx_sflash_struct_bcma_init(&bcm47xx_sflash, &bcm47xx_bus.bcma.bus.drv_cc); ++ ++ if (bcm47xx_bus.bcma.bus.drv_cc.flash_type == BCMA_NFLASH) ++ bcm47xx_nflash_struct_bcma_init(&bcm47xx_nflash, &bcm47xx_bus.bcma.bus.drv_cc); + } + + static int __init bcm47xx_register_flash_bcma(void) +@@ -383,6 +401,9 @@ static int __init bcm47xx_register_flash + case BCMA_SFLASH: + bcm47xx_sflash_dev.dev.platform_data = &bcm47xx_sflash; + return platform_device_register(&bcm47xx_sflash_dev); ++ case BCMA_NFLASH: ++ bcm47xx_nflash_dev.dev.platform_data = &bcm47xx_nflash; ++ return platform_device_register(&bcm47xx_nflash_dev); + default: + printk(KERN_ERR "No flash device found\n"); + return -1; +--- a/arch/mips/include/asm/mach-bcm47xx/bus.h ++++ b/arch/mips/include/asm/mach-bcm47xx/bus.h +@@ -2,6 +2,7 @@ + * BCM947xx nvram variable access + * + * Copyright (C) 2011 Hauke Mehrtens ++ * Copyright (C) 2011-2012 Tathagata Das + * + * 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 +@@ -13,6 +14,7 @@ + #include + #include + #include ++#include + + struct bcm47xx_sflash { + enum bcm47xx_bus_type sflash_type; +@@ -29,11 +31,24 @@ struct bcm47xx_sflash { + u32 blocksize; /* Block size */ + u32 numblocks; /* Number of blocks */ + u32 size; /* Total size in bytes */ +- +- struct mtd_info *mtd; + }; + + void bcm47xx_sflash_struct_bcma_init(struct bcm47xx_sflash *sflash, struct bcma_drv_cc *bcc); + void bcm47xx_sflash_struct_ssb_init(struct bcm47xx_sflash *sflash, struct ssb_chipcommon *scc); + + extern struct bcm47xx_sflash bcm47xx_sflash; ++ ++struct bcm47xx_nflash { ++ enum bcm47xx_bus_type nflash_type; ++ struct bcma_drv_cc *bcc; ++ ++ u32 size; /* Total size in bytes */ ++ u32 next_opcode; /* Next expected command from upper NAND layer */ ++ ++ struct mtd_info mtd; ++ struct nand_chip nand; ++}; ++ ++void bcm47xx_nflash_struct_bcma_init(struct bcm47xx_nflash *nflash, struct bcma_drv_cc *bcc); ++ ++extern struct bcm47xx_nflash bcm47xx_nflash; +--- a/drivers/bcma/Kconfig ++++ b/drivers/bcma/Kconfig +@@ -43,6 +43,11 @@ config BCMA_SFLASH + depends on BCMA_DRIVER_MIPS + default y + ++config BCMA_NFLASH ++ bool ++ depends on BCMA_DRIVER_MIPS ++ default y ++ + config BCMA_DRIVER_MIPS + bool "BCMA Broadcom MIPS core driver" + depends on BCMA && MIPS +--- a/drivers/bcma/Makefile ++++ b/drivers/bcma/Makefile +@@ -1,6 +1,7 @@ + bcma-y += main.o scan.o core.o sprom.o + bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o + bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o ++bcma-$(CONFIG_BCMA_NFLASH) += driver_chipcommon_nflash.o + bcma-y += driver_pci.o + bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o + bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o +--- a/drivers/bcma/bcma_private.h ++++ b/drivers/bcma/bcma_private.h +@@ -47,6 +47,11 @@ u32 bcma_pmu_get_clockcpu(struct bcma_dr + int bcma_sflash_init(struct bcma_drv_cc *cc); + #endif /* CONFIG_BCMA_SFLASH */ + ++#ifdef CONFIG_BCMA_NFLASH ++/* driver_chipcommon_nflash.c */ ++int bcma_nflash_init(struct bcma_drv_cc *cc); ++#endif /* CONFIG_BCMA_NFLASH */ ++ + #ifdef CONFIG_BCMA_HOST_PCI + /* host_pci.c */ + extern int __init bcma_host_pci_init(void); +--- /dev/null ++++ b/drivers/bcma/driver_chipcommon_nflash.c +@@ -0,0 +1,154 @@ ++/* ++ * BCMA nand flash interface ++ * ++ * Copyright 2011, Tathagata Das ++ * Copyright 2010, Broadcom Corporation ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "bcma_private.h" ++ ++/* Issue a nand flash command */ ++static inline void bcma_nflash_cmd(struct bcma_drv_cc *cc, u32 opcode) ++{ ++ bcma_cc_write32(cc, NAND_CMD_START, opcode); ++ bcma_cc_read32(cc, NAND_CMD_START); ++} ++ ++/* Check offset and length */ ++static int bcma_nflash_offset_is_valid(struct bcma_drv_cc *cc, u32 offset, u32 len, u32 mask) ++{ ++ if ((offset & mask) != 0 || (len & mask) != 0) { ++ pr_err("%s(): Address is not aligned. offset: %x, len: %x, mask: %x\n", __func__, offset, len, mask); ++ return 1; ++ } ++ ++ if ((((offset + len) >> 20) >= cc->nflash.size) && ++ (((offset + len) & ((1 << 20) - 1)) != 0)) { ++ pr_err("%s(): Address is outside Flash memory region. offset: %x, len: %x, mask: %x\n", __func__, offset, len, mask); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* Read len bytes starting at offset into buf. Returns number of bytes read. */ ++int bcma_nflash_read(struct bcma_drv_cc *cc, u32 offset, u32 len, u8 *buf) ++{ ++ u32 mask; ++ int i; ++ u32 *to, val, res; ++ ++ mask = NFL_SECTOR_SIZE - 1; ++ if (bcma_nflash_offset_is_valid(cc, offset, len, mask)) ++ return 0; ++ ++ to = (u32 *)buf; ++ res = len; ++ while (res > 0) { ++ bcma_cc_write32(cc, NAND_CMD_ADDR, offset); ++ bcma_nflash_cmd(cc, NCMD_PAGE_RD); ++ if (bcma_nflash_poll(cc) < 0) ++ break; ++ val = bcma_cc_read32(cc, NAND_INTFC_STATUS); ++ if ((val & NIST_CACHE_VALID) == 0) ++ break; ++ bcma_cc_write32(cc, NAND_CACHE_ADDR, 0); ++ for (i = 0; i < NFL_SECTOR_SIZE; i += 4, to++) { ++ *to = bcma_cc_read32(cc, NAND_CACHE_DATA); ++ } ++ res -= NFL_SECTOR_SIZE; ++ offset += NFL_SECTOR_SIZE; ++ } ++ return (len - res); ++} ++ ++#define NF_RETRIES 1000000 ++ ++/* Poll for command completion. Returns zero when complete. */ ++int bcma_nflash_poll(struct bcma_drv_cc *cc) ++{ ++ u32 retries = NF_RETRIES; ++ u32 pollmask = NIST_CTRL_READY|NIST_FLASH_READY; ++ u32 mask; ++ ++ while (retries--) { ++ mask = bcma_cc_read32(cc, NAND_INTFC_STATUS) & pollmask; ++ if (mask == pollmask) ++ return 0; ++ cpu_relax(); ++ } ++ ++ if (!retries) { ++ pr_err("bcma_nflash_poll: not ready\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* Write len bytes starting at offset into buf. Returns success (0) or failure (!0). ++ * Should poll for completion. ++ */ ++int bcma_nflash_write(struct bcma_drv_cc *cc, u32 offset, u32 len, ++ const u8 *buf) ++{ ++ u32 mask; ++ int i; ++ u32 *from, res, reg; ++ ++ mask = cc->nflash.pagesize - 1; ++ if (bcma_nflash_offset_is_valid(cc, offset, len, mask)) ++ return 1; ++ ++ /* disable partial page enable */ ++ reg = bcma_cc_read32(cc, NAND_ACC_CONTROL); ++ reg &= ~NAC_PARTIAL_PAGE_EN; ++ bcma_cc_write32(cc, NAND_ACC_CONTROL, reg); ++ ++ from = (u32 *)buf; ++ res = len; ++ while (res > 0) { ++ bcma_cc_write32(cc, NAND_CACHE_ADDR, 0); ++ for (i = 0; i < cc->nflash.pagesize; i += 4, from++) { ++ if (i % 512 == 0) ++ bcma_cc_write32(cc, NAND_CMD_ADDR, i); ++ bcma_cc_write32(cc, NAND_CACHE_DATA, *from); ++ } ++ bcma_cc_write32(cc, NAND_CMD_ADDR, offset + cc->nflash.pagesize - 512); ++ bcma_nflash_cmd(cc, NCMD_PAGE_PROG); ++ if (bcma_nflash_poll(cc) < 0) ++ break; ++ res -= cc->nflash.pagesize; ++ offset += cc->nflash.pagesize; ++ } ++ ++ if (res <= 0) ++ return 0; ++ else ++ return (len - res); ++} ++ ++/* Erase a region. Returns success (0) or failure (-1). ++ * Poll for completion. ++ */ ++int bcma_nflash_erase(struct bcma_drv_cc *cc, u32 offset) ++{ ++ if ((offset >> 20) >= cc->nflash.size) ++ return -1; ++ if ((offset & (cc->nflash.blocksize - 1)) != 0) ++ return -1; ++ ++ bcma_cc_write32(cc, NAND_CMD_ADDR, offset); ++ bcma_nflash_cmd(cc, NCMD_BLOCK_ERASE); ++ if (bcma_nflash_poll(cc) < 0) ++ return -1; ++ return 0; ++} +--- a/drivers/bcma/driver_mips.c ++++ b/drivers/bcma/driver_mips.c +@@ -6,6 +6,7 @@ + * Copyright 2006, 2007, Michael Buesch + * Copyright 2010, Bernhard Loos + * Copyright 2011, Hauke Mehrtens ++ * Copyright (C) 2011-2012 Tathagata Das + * + * Licensed under the GNU/GPL. See COPYING for details. + */ +@@ -182,6 +183,17 @@ static void bcma_core_mips_flash_detect( + { + struct bcma_bus *bus = mcore->core->bus; + ++ if (bus->drv_cc.core->id.rev == 38 ++ && (bus->drv_cc.status & (1 << 4)) != 0) { ++#ifdef CONFIG_BCMA_NFLASH ++ pr_info("found nand flash.\n"); ++ bus->drv_cc.flash_type = BCMA_NFLASH; ++#else ++ pr_info("NAND flash not supported.\n"); ++#endif ++ return; ++ } ++ + switch (bus->drv_cc.capabilities & BCMA_CC_CAP_FLASHT) { + case BCMA_CC_FLASHT_STSER: + case BCMA_CC_FLASHT_ATSER: +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -536,4 +536,12 @@ config MTD_NAND_FSMC + Enables support for NAND Flash chips on the ST Microelectronics + Flexible Static Memory Controller (FSMC) + ++config MTD_NAND_BCM47XX ++ tristate "bcm47xx nand flash support" ++ default y ++ depends on BCM47XX ++ select MTD_PARTITIONS ++ help ++ Support for bcm47xx nand flash ++ + endif # MTD_NAND +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -49,5 +49,6 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mp + obj-$(CONFIG_MTD_NAND_RICOH) += r852.o + obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o + obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ ++obj-$(CONFIG_MTD_NAND_BCM47XX) += bcm47xx_nand.o + + nand-objs := nand_base.o nand_bbt.o +--- /dev/null ++++ b/drivers/mtd/nand/bcm47xx_nand.c +@@ -0,0 +1,506 @@ ++/* ++ * BCMA nand flash interface ++ * ++ * Copyright (C) 2011-2012 Tathagata Das ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int bcm47xx_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip); ++static int bcm47xx_erase(struct mtd_info *mtd, unsigned int addr, unsigned int len); ++ ++/* Private Global variable */ ++static u32 read_offset = 0; ++static u32 write_offset; ++ ++static int ++nflash_mtd_poll(struct bcm47xx_nflash *nflash, unsigned int offset, int timeout) ++{ ++ unsigned long now = jiffies; ++ int ret = 0; ++ ++ for (;;) { ++ if (!bcma_nflash_poll(nflash->bcc)) { ++ ret = 0; ++ break; ++ } ++ if (time_after(jiffies, now + timeout)) { ++ pr_err("timeout while polling\n"); ++ ret = -ETIMEDOUT; ++ break; ++ } ++ udelay(1); ++ } ++ ++ return ret; ++} ++ ++static int ++bcm47xx_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) ++{ ++ struct nand_chip *nchip = (struct nand_chip *)mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ int bytes, ret = 0; ++ u32 extra = 0; ++ u8 *tmpbuf = NULL; ++ int size; ++ u32 offset, blocksize, mask, off; ++ u32 skip_bytes = 0; ++ int need_copy = 0; ++ u8 *ptr = NULL; ++ ++ /* Check address range */ ++ if (!len) ++ return 0; ++ if ((from + len) > mtd->size) ++ return -EINVAL; ++ offset = from; ++ if ((offset & (NFL_SECTOR_SIZE - 1)) != 0) { ++ extra = offset & (NFL_SECTOR_SIZE - 1); ++ offset -= extra; ++ len += extra; ++ need_copy = 1; ++ } ++ size = (len + (NFL_SECTOR_SIZE - 1)) & ~(NFL_SECTOR_SIZE - 1); ++ if (size != len) { ++ need_copy = 1; ++ } ++ if (!need_copy) { ++ ptr = buf; ++ } else { ++ tmpbuf = (u8 *)kmalloc(size, GFP_KERNEL); ++ ptr = tmpbuf; ++ } ++ ++ blocksize = mtd->erasesize; ++ mask = blocksize - 1; ++ *retlen = 0; ++ while (len > 0) { ++ off = offset + skip_bytes; ++ if ((bytes = bcma_nflash_read(nflash->bcc, off, NFL_SECTOR_SIZE, ptr)) < 0) { ++ ret = bytes; ++ goto done; ++ } ++ if (bytes > len) ++ bytes = len; ++ offset += bytes; ++ len -= bytes; ++ ptr += bytes; ++ *retlen += bytes; ++ } ++ ++done: ++ if (tmpbuf) { ++ *retlen -= extra; ++ memcpy(buf, tmpbuf+extra, *retlen); ++ kfree(tmpbuf); ++ } ++ ++ return ret; ++} ++ ++static void bcm47xx_write(struct mtd_info *mtd, u32 to, const u_char *buf, u32 len) ++{ ++ struct nand_chip *nchip = (struct nand_chip *)mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ u32 offset, blocksize, mask, off; ++ int read_len; ++ u32 copy_len, write_len, from; ++ u_char *write_ptr, *block; ++ const u_char *ptr; ++ int ret, bytes; ++ ++ /* Check address range */ ++ if (!len) { ++ pr_err("Error: Attempted to write too small data\n"); ++ return; ++ } ++ ++ if (!to) ++ return; ++ ++ if ((to + len) > mtd->size) { ++ pr_err("Error: Attempted to write too large data\n"); ++ return; ++ } ++ ++ ptr = buf; ++ block = NULL; ++ offset = to; ++ blocksize = mtd->erasesize; ++ if (!(block = kmalloc(blocksize, GFP_KERNEL))) ++ return; ++ mask = blocksize - 1; ++ while (len) { ++ /* Align offset */ ++ from = offset & ~mask; ++ /* Copy existing data into holding block if necessary */ ++ if (((offset & (blocksize-1)) != 0) || (len < blocksize)) { ++ if ((ret = bcm47xx_read(mtd, from, blocksize, &read_len, block))) ++ goto done; ++ if (read_len != blocksize) { ++ ret = -EINVAL; ++ goto done; ++ } ++ } ++ ++ /* Copy input data into holding block */ ++ copy_len = min(len, blocksize - (offset & mask)); ++ memcpy(block + (offset & mask), ptr, copy_len); ++ off = (uint) from; ++ /* Erase block */ ++ if ((ret = bcm47xx_erase(mtd, off, blocksize)) < 0) ++ goto done; ++ /* Write holding block */ ++ write_ptr = block; ++ write_len = blocksize; ++ if ((bytes = bcma_nflash_write(nflash->bcc, (uint)from, (uint)write_len, (u8 *) write_ptr)) != 0) { ++ ret = bytes; ++ goto done; ++ } ++ offset += copy_len; ++ if (len < copy_len) ++ len = 0; ++ else ++ len -= copy_len; ++ ptr += copy_len; ++ } ++ ++done: ++ if (block) ++ kfree(block); ++ return; ++} ++ ++static int bcm47xx_erase(struct mtd_info *mtd, unsigned int addr, unsigned int len) ++{ ++ struct nand_chip *nchip = (struct nand_chip *)mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ ++ /* Check address range */ ++ if (!len) ++ return 1; ++ if ((addr + len) > mtd->size) ++ return 1; ++ ++ if (bcma_nflash_erase(nflash->bcc, addr)) { ++ pr_err("ERASE: nflash erase error\n"); ++ return 1; ++ } ++ ++ if (nflash_mtd_poll(nflash, addr, 10 * HZ)) { ++ pr_err("ERASE: nflash_mtd_poll error\n"); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* This functions is used by upper layer to checks if device is ready */ ++static int bcm47xx_dev_ready(struct mtd_info *mtd) ++{ ++ return 1; ++} ++ ++/* Issue a nand flash command */ ++static inline void bcm47xx_nflash_cmd(struct bcma_drv_cc *cc, u32 opcode) ++{ ++ bcma_cc_write32(cc, NAND_CMD_START, opcode); ++ bcma_cc_read32(cc, NAND_CMD_START); ++} ++ ++static void bcm47xx_command(struct mtd_info *mtd, unsigned command, ++ int column, int page_addr) ++{ ++ struct nand_chip *nchip = (struct nand_chip *)mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ u32 pagesize = 1 << nchip->page_shift; ++ ++ /* Command pre-processing step */ ++ switch (command) { ++ case NAND_CMD_RESET: ++ bcm47xx_nflash_cmd(nflash->bcc, NCMD_FLASH_RESET); ++ break; ++ ++ case NAND_CMD_STATUS: ++ nflash->next_opcode = NAND_CMD_STATUS; ++ read_offset = 0; ++ write_offset = 0; ++ break; ++ ++ case NAND_CMD_READ0: ++ read_offset = page_addr * pagesize; ++ nflash->next_opcode = 0; ++ break; ++ ++ case NAND_CMD_READOOB: ++ read_offset = page_addr * pagesize; ++ nflash->next_opcode = 0; ++ break; ++ ++ case NAND_CMD_SEQIN: ++ write_offset = page_addr * pagesize; ++ nflash->next_opcode = 0; ++ break; ++ ++ case NAND_CMD_PAGEPROG: ++ nflash->next_opcode = 0; ++ break; ++ ++ case NAND_CMD_READID: ++ read_offset = column; ++ bcm47xx_nflash_cmd(nflash->bcc, NCMD_ID_RD); ++ nflash->next_opcode = NAND_DEVID; ++ break; ++ ++ case NAND_CMD_ERASE1: ++ nflash->next_opcode = 0; ++ bcm47xx_erase(mtd, page_addr*pagesize, pagesize); ++ break; ++ ++ case NAND_CMD_ERASE2: ++ break; ++ ++ case NAND_CMD_RNDOUT: ++ if (column > mtd->writesize) ++ read_offset += (column - mtd->writesize); ++ else ++ read_offset += column; ++ break; ++ ++ default: ++ pr_err("COMMAND not supported %x\n", command); ++ nflash->next_opcode = 0; ++ break; ++ } ++} ++ ++/* This function is used by upper layer for select and ++ * deselect of the NAND chip. ++ * It is dummy function. */ ++static void bcm47xx_select_chip(struct mtd_info *mtd, int chip) ++{ ++} ++ ++static u_char bcm47xx_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *nchip = mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ uint8_t ret = 0; ++ static u32 id; ++ ++ if (nflash->next_opcode == 0) ++ return ret; ++ ++ if (nflash->next_opcode == NAND_CMD_STATUS) ++ return NAND_STATUS_WP; ++ ++ id = bcma_cc_read32(nflash->bcc, nflash->next_opcode); ++ ++ if (nflash->next_opcode == NAND_DEVID) { ++ ret = (id >> (8*read_offset)) & 0xff; ++ read_offset++; ++ } ++ ++ return ret; ++} ++ ++static uint16_t bcm47xx_read_word(struct mtd_info *mtd) ++{ ++ loff_t from = read_offset; ++ uint16_t buf = 0; ++ int bytes; ++ ++ bcm47xx_read(mtd, from, sizeof(buf), &bytes, (u_char *)&buf); ++ return buf; ++} ++ ++/* Write data of length len to buffer buf. The data to be ++ * written on NAND Flash is first copied to RAMbuffer. After the Data Input ++ * Operation by the NFC, the data is written to NAND Flash */ ++static void bcm47xx_write_buf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ bcm47xx_write(mtd, write_offset, buf, len); ++} ++ ++/* Read the data buffer from the NAND Flash. To read the data from NAND ++ * Flash first the data output cycle is initiated by the NFC, which copies ++ * the data to RAMbuffer. This data of length len is then copied to buffer buf. ++ */ ++static void bcm47xx_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ loff_t from = read_offset; ++ int bytes; ++ ++ bcm47xx_read(mtd, from, len, &bytes, buf); ++} ++ ++/* Used by the upper layer to verify the data in NAND Flash ++ * with the data in the buf. */ ++static int bcm47xx_verify_buf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ return -EFAULT; ++} ++ ++static int bcm47xx_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) ++{ ++ struct nand_chip *nchip = mtd->priv; ++ struct bcm47xx_nflash *nflash = (struct bcm47xx_nflash *)nchip->priv; ++ int i; ++ uint off; ++ u32 pagesize = 1 << nchip->page_shift; ++ u32 blocksize = mtd->erasesize; ++ ++ if ((ofs >> 20) >= nflash->size) ++ return 1; ++ if ((ofs & (blocksize - 1)) != 0) ++ return 1; ++ ++ for (i = 0; i < 2; i++) { ++ off = ofs + pagesize; ++ bcma_cc_write32(nflash->bcc, NAND_CMD_ADDR, off); ++ bcm47xx_nflash_cmd(nflash->bcc, NCMD_SPARE_RD); ++ if (bcma_nflash_poll(nflash->bcc) < 0) ++ break; ++ if ((bcma_cc_read32(nflash->bcc, NAND_INTFC_STATUS) & NIST_SPARE_VALID) != NIST_SPARE_VALID) ++ return 1; ++ if ((bcma_cc_read32(nflash->bcc, NAND_SPARE_RD0) & 0xff) != 0xff) ++ return 1; ++ } ++ return 0; ++} ++ ++const char *part_probes[] = { "cmdlinepart", NULL }; ++static int bcm47xx_probe(struct platform_device *pdev) ++{ ++ struct nand_chip *nchip; ++ struct mtd_info *mtd; ++ struct bcm47xx_nflash *nflash = dev_get_platdata(&pdev->dev); ++ int ret = 0; ++ ++ mtd = &nflash->mtd; ++ nchip = &nflash->nand; ++ ++ /* Register with MTD */ ++ mtd->priv = nchip; ++ mtd->owner = THIS_MODULE; ++ mtd->dev.parent = &pdev->dev; ++ ++ /* 50 us command delay time */ ++ nchip->chip_delay = 50; ++ ++ nchip->priv = nflash; ++ nchip->dev_ready = bcm47xx_dev_ready; ++ nchip->cmdfunc = bcm47xx_command; ++ nchip->select_chip = bcm47xx_select_chip; ++ nchip->read_byte = bcm47xx_read_byte; ++ nchip->read_word = bcm47xx_read_word; ++ nchip->write_buf = bcm47xx_write_buf; ++ nchip->read_buf = bcm47xx_read_buf; ++ nchip->verify_buf = bcm47xx_verify_buf; ++ nchip->block_bad = bcm47xx_block_bad; ++ nchip->options = NAND_SKIP_BBTSCAN; ++ ++ /* Not known */ ++ nchip->ecc.mode = NAND_ECC_NONE; ++ ++ /* first scan to find the device and get the page size */ ++ if (nand_scan_ident(mtd, 1, NULL)) { ++ pr_err("nand_scan_ident failed\n"); ++ ret = -ENXIO; ++ goto done; ++ } ++ nflash->bcc->nflash.size = mtd->size; ++ nflash->bcc->nflash.pagesize = 1 << nchip->page_shift; ++ nflash->bcc->nflash.blocksize = mtd->erasesize; ++ bcm47xx_nflash.size = mtd->size; ++ ++ /* second phase scan */ ++ if (nand_scan_tail(mtd)) { ++ pr_err("nand_scan_tail failed\n"); ++ ret = -ENXIO; ++ goto done; ++ } ++ ++ mtd->name = "bcm47xx-nflash"; ++ mtd->flags |= MTD_WRITEABLE; ++ ret = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); ++ ++ if (ret) { ++ pr_err("mtd_device_register failed\n"); ++ return ret; ++ } ++ ++ return 0; ++ ++done: ++ return ret; ++} ++ ++static int __devexit bcm47xx_remove(struct platform_device *pdev) ++{ ++ struct bcm47xx_nflash *nflash = dev_get_platdata(&pdev->dev); ++ struct mtd_info *mtd = &nflash->mtd; ++ ++ if (nflash) { ++ /* Release resources, unregister device */ ++ nand_release(mtd); ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver bcm47xx_driver = { ++ .remove = __devexit_p(bcm47xx_remove), ++ .driver = { ++ .name = "bcm47xx_nflash", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init init_bcm47xx_nflash(void) ++{ ++ int ret = platform_driver_probe(&bcm47xx_driver, bcm47xx_probe); ++ ++ if (ret) ++ pr_err("error registering platform driver: %i\n", ret); ++ return ret; ++} ++ ++static void __exit exit_bcm47xx_nflash(void) ++{ ++ platform_driver_unregister(&bcm47xx_driver); ++} ++ ++module_init(init_bcm47xx_nflash); ++module_exit(exit_bcm47xx_nflash); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("BCM47XX NAND flash driver"); +--- a/include/linux/bcma/bcma_driver_chipcommon.h ++++ b/include/linux/bcma/bcma_driver_chipcommon.h +@@ -400,6 +400,7 @@ struct bcma_chipcommon_pmu { + enum bcma_flash_type { + BCMA_PFLASH, + BCMA_SFLASH, ++ BCMA_NFLASH, + }; + + struct bcma_pflash { +@@ -416,6 +417,14 @@ struct bcma_sflash { + }; + #endif /* CONFIG_BCMA_SFLASH */ + ++#ifdef CONFIG_BCMA_NFLASH ++struct bcma_nflash { ++ u32 blocksize; /* Block size */ ++ u32 pagesize; /* Page size */ ++ u32 size; /* Total size in bytes */ ++}; ++#endif ++ + struct bcma_serial_port { + void *regs; + unsigned long clockspeed; +@@ -441,6 +450,9 @@ struct bcma_drv_cc { + #ifdef CONFIG_BCMA_SFLASH + struct bcma_sflash sflash; + #endif /* CONFIG_BCMA_SFLASH */ ++#ifdef CONFIG_BCMA_NFLASH ++ struct bcma_nflash nflash; ++#endif + }; + + int nr_serial_ports; +@@ -505,4 +517,13 @@ int bcma_sflash_write(struct bcma_drv_cc + int bcma_sflash_erase(struct bcma_drv_cc *cc, u32 offset); + #endif /* CONFIG_BCMA_SFLASH */ + ++#ifdef CONFIG_BCMA_NFLASH ++/* Chipcommon nflash support. */ ++int bcma_nflash_read(struct bcma_drv_cc *cc, u32 offset, u32 len, u8 *buf); ++int bcma_nflash_poll(struct bcma_drv_cc *cc); ++int bcma_nflash_write(struct bcma_drv_cc *cc, u32 offset, u32 len, const u8 *buf); ++int bcma_nflash_erase(struct bcma_drv_cc *cc, u32 offset); ++int bcma_nflash_commit(struct bcma_drv_cc *cc, u32 offset, u32 len, const u8 *buf); ++#endif ++ + #endif /* LINUX_BCMA_DRIVER_CC_H_ */ +--- /dev/null ++++ b/include/linux/mtd/bcm47xx_nand.h +@@ -0,0 +1,134 @@ ++/* ++ * Broadcom chipcommon NAND flash interface ++ * ++ * Copyright (C) 2011-2012 Tathagata Das ++ * Copyright (C) 2009, Broadcom Corporation ++ * All Rights Reserved. ++ * ++ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY ++ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM ++ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS ++ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. ++ * ++ */ ++ ++#ifndef _nflash_h_ ++#define _nflash_h_ ++ ++#define NAND_FLASH1 0x1fc00000 /* MIPS Flash Region 1 */ ++ ++/* nand_cmd_start commands */ ++#define NCMD_NULL 0 ++#define NCMD_PAGE_RD 1 ++#define NCMD_SPARE_RD 2 ++#define NCMD_STATUS_RD 3 ++#define NCMD_PAGE_PROG 4 ++#define NCMD_SPARE_PROG 5 ++#define NCMD_COPY_BACK 6 ++#define NCMD_ID_RD 7 ++#define NCMD_BLOCK_ERASE 8 ++#define NCMD_FLASH_RESET 9 ++#define NCMD_LOCK 0xa ++#define NCMD_LOCK_DOWN 0xb ++#define NCMD_UNLOCK 0xc ++#define NCMD_LOCK_STATUS 0xd ++ ++/* nand_acc_control */ ++#define NAC_RD_ECC_EN 0x80000000 ++#define NAC_WR_ECC_EN 0x40000000 ++#define NAC_RD_ECC_BLK0_EN 0x20000000 ++#define NAC_FAST_PGM_RDIN 0x10000000 ++#define NAC_RD_ERASED_ECC_EN 0x08000000 ++#define NAC_PARTIAL_PAGE_EN 0x04000000 ++#define NAC_PAGE_HIT_EN 0x01000000 ++#define NAC_ECC_LEVEL0 0x00f00000 ++#define NAC_ECC_LEVEL 0x000f0000 ++#define NAC_SPARE_SIZE0 0x00003f00 ++#define NAC_SPARE_SIZE 0x0000003f ++ ++/* nand_config */ ++#define NCF_CONFIG_LOCK 0x80000000 ++#define NCF_BLOCK_SIZE_MASK 0x70000000 ++#define NCF_BLOCK_SIZE_SHIFT 28 ++#define NCF_DEVICE_SIZE_MASK 0x0f000000 ++#define NCF_DEVICE_SIZE_SHIFT 24 ++#define NCF_DEVICE_WIDTH 0x00800000 ++#define NCF_PAGE_SIZE_MASK 0x00300000 ++#define NCF_PAGE_SIZE_SHIFT 20 ++#define NCF_FULL_ADDR_BYTES_MASK 0x00070000 ++#define NCF_FULL_ADDR_BYTES_SHIFT 16 ++#define NCF_COL_ADDR_BYTES_MASK 0x00007000 ++#define NCF_COL_ADDR_BYTES_SHIFT 12 ++#define NCF_BLK_ADDR_BYTES_MASK 0x00000700 ++#define NCF_BLK_ADDR_BYTES_SHIFT 8 ++ ++/* nand_intfc_status */ ++#define NIST_CTRL_READY 0x80000000 ++#define NIST_FLASH_READY 0x40000000 ++#define NIST_CACHE_VALID 0x20000000 ++#define NIST_SPARE_VALID 0x10000000 ++#define NIST_ERASED 0x08000000 ++#define NIST_STATUS 0x000000ff ++ ++#define NFL_SECTOR_SIZE 512 ++ ++#define NFL_TABLE_END 0xffffffff ++#define NFL_BOOT_SIZE 0x200000 ++#define NFL_BOOT_OS_SIZE 0x2000000 ++ ++/* Nand flash MLC controller registers (corerev >= 38) */ ++#define NAND_REVISION 0xC00 ++#define NAND_CMD_START 0xC04 ++#define NAND_CMD_ADDR_X 0xC08 ++#define NAND_CMD_ADDR 0xC0C ++#define NAND_CMD_END_ADDR 0xC10 ++#define NAND_CS_NAND_SELECT 0xC14 ++#define NAND_CS_NAND_XOR 0xC18 ++#define NAND_SPARE_RD0 0xC20 ++#define NAND_SPARE_RD4 0xC24 ++#define NAND_SPARE_RD8 0xC28 ++#define NAND_SPARE_RD12 0xC2C ++#define NAND_SPARE_WR0 0xC30 ++#define NAND_SPARE_WR4 0xC34 ++#define NAND_SPARE_WR8 0xC38 ++#define NAND_SPARE_WR12 0xC3C ++#define NAND_ACC_CONTROL 0xC40 ++#define NAND_CONFIG 0xC48 ++#define NAND_TIMING_1 0xC50 ++#define NAND_TIMING_2 0xC54 ++#define NAND_SEMAPHORE 0xC58 ++#define NAND_DEVID 0xC60 ++#define NAND_DEVID_X 0xC64 ++#define NAND_BLOCK_LOCK_STATUS 0xC68 ++#define NAND_INTFC_STATUS 0xC6C ++#define NAND_ECC_CORR_ADDR_X 0xC70 ++#define NAND_ECC_CORR_ADDR 0xC74 ++#define NAND_ECC_UNC_ADDR_X 0xC78 ++#define NAND_ECC_UNC_ADDR 0xC7C ++#define NAND_READ_ERROR_COUNT 0xC80 ++#define NAND_CORR_STAT_THRESHOLD 0xC84 ++#define NAND_READ_ADDR_X 0xC90 ++#define NAND_READ_ADDR 0xC94 ++#define NAND_PAGE_PROGRAM_ADDR_X 0xC98 ++#define NAND_PAGE_PROGRAM_ADDR 0xC9C ++#define NAND_COPY_BACK_ADDR_X 0xCA0 ++#define NAND_COPY_BACK_ADDR 0xCA4 ++#define NAND_BLOCK_ERASE_ADDR_X 0xCA8 ++#define NAND_BLOCK_ERASE_ADDR 0xCAC ++#define NAND_INV_READ_ADDR_X 0xCB0 ++#define NAND_INV_READ_ADDR 0xCB4 ++#define NAND_BLK_WR_PROTECT 0xCC0 ++#define NAND_ACC_CONTROL_CS1 0xCD0 ++#define NAND_CONFIG_CS1 0xCD4 ++#define NAND_TIMING_1_CS1 0xCD8 ++#define NAND_TIMING_2_CS1 0xCDC ++#define NAND_SPARE_RD16 0xD30 ++#define NAND_SPARE_RD20 0xD34 ++#define NAND_SPARE_RD24 0xD38 ++#define NAND_SPARE_RD28 0xD3C ++#define NAND_CACHE_ADDR 0xD40 ++#define NAND_CACHE_DATA 0xD44 ++#define NAND_CTRL_CONFIG 0xD48 ++#define NAND_CTRL_STATUS 0xD4C ++ ++#endif /* _nflash_h_ */ diff --git a/target/linux/brcm47xx/patches-3.3/044-bcma-add-PCIe-host-controller.patch b/target/linux/brcm47xx/patches-3.3/044-bcma-add-PCIe-host-controller.patch new file mode 100644 index 0000000000..e5740693f1 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/044-bcma-add-PCIe-host-controller.patch @@ -0,0 +1,76 @@ +--- a/arch/mips/pci/pci-bcm47xx.c ++++ b/arch/mips/pci/pci-bcm47xx.c +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + + int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +@@ -32,15 +33,12 @@ int __init pcibios_map_irq(const struct + return 0; + } + +-int pcibios_plat_dev_init(struct pci_dev *dev) +-{ + #ifdef CONFIG_BCM47XX_SSB ++static int bcm47xx_pcibios_plat_dev_init_ssb(struct pci_dev *dev) ++{ + int res; + u8 slot, pin; + +- if (bcm47xx_bus_type != BCM47XX_BUS_TYPE_SSB) +- return 0; +- + res = ssb_pcibios_plat_dev_init(dev); + if (res < 0) { + printk(KERN_ALERT "PCI: Failed to init device %s\n", +@@ -60,6 +58,47 @@ int pcibios_plat_dev_init(struct pci_dev + } + + dev->irq = res; ++ return 0; ++} + #endif ++ ++#ifdef CONFIG_BCM47XX_BCMA ++static int bcm47xx_pcibios_plat_dev_init_bcma(struct pci_dev *dev) ++{ ++ int res; ++ ++ res = bcma_core_pci_plat_dev_init(dev); ++ if (res < 0) { ++ printk(KERN_ALERT "PCI: Failed to init device %s\n", ++ pci_name(dev)); ++ return res; ++ } ++ ++ res = bcma_core_pci_pcibios_map_irq(dev); ++ ++ /* IRQ-0 and IRQ-1 are software interrupts. */ ++ if (res < 2) { ++ printk(KERN_ALERT "PCI: Failed to map IRQ of device %s\n", ++ pci_name(dev)); ++ return res; ++ } ++ ++ dev->irq = res; + return 0; + } ++#endif ++ ++int pcibios_plat_dev_init(struct pci_dev *dev) ++{ ++#ifdef CONFIG_BCM47XX_SSB ++ if (bcm47xx_bus_type == BCM47XX_BUS_TYPE_SSB) ++ return bcm47xx_pcibios_plat_dev_init_ssb(dev); ++ else ++#endif ++#ifdef CONFIG_BCM47XX_BCMA ++ if (bcm47xx_bus_type == BCM47XX_BUS_TYPE_BCMA) ++ return bcm47xx_pcibios_plat_dev_init_bcma(dev); ++ else ++#endif ++ return 0; ++} diff --git a/target/linux/brcm47xx/patches-3.3/047-bcma-add-new-PCI-ID.patch b/target/linux/brcm47xx/patches-3.3/047-bcma-add-new-PCI-ID.patch new file mode 100644 index 0000000000..77062c3dab --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/047-bcma-add-new-PCI-ID.patch @@ -0,0 +1,24 @@ +From 699c57a18b40ffbe1763915acdc1a3e4fb539d01 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Fri, 13 Jan 2012 17:42:15 +0100 +Subject: [PATCH 29/32] bcma: add new PCI ID + +This ID was found on the PCIe wireless card on the board of a Netgear +WNDR3400 using a bcm4716. The device with this ID is identified by b43 +as "Broadcom 43224 WLAN". + +Signed-off-by: Hauke Mehrtens +--- + drivers/bcma/host_pci.c | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +--- a/drivers/bcma/host_pci.c ++++ b/drivers/bcma/host_pci.c +@@ -269,6 +269,7 @@ static DEFINE_PCI_DEVICE_TABLE(bcma_pci_ + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) }, ++ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0xa8d8) }, + { 0, }, + }; + MODULE_DEVICE_TABLE(pci, bcma_pci_bridge_tbl); diff --git a/target/linux/brcm47xx/patches-3.3/050-bcma-export-needed-gpio-functions.patch b/target/linux/brcm47xx/patches-3.3/050-bcma-export-needed-gpio-functions.patch new file mode 100644 index 0000000000..7d172d4c93 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/050-bcma-export-needed-gpio-functions.patch @@ -0,0 +1,47 @@ +From f6e41db3ee7ead99e1398def222c14893fc265de Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Thu, 4 Aug 2011 21:09:48 +0200 +Subject: [PATCH 26/26] bcma: export needed gpio functions + + +Signed-off-by: Hauke Mehrtens +--- + drivers/bcma/driver_chipcommon.c | 5 +++++ + 1 files changed, 5 insertions(+), 0 deletions(-) + +--- a/drivers/bcma/driver_chipcommon.c ++++ b/drivers/bcma/driver_chipcommon.c +@@ -81,16 +81,19 @@ u32 bcma_chipco_gpio_in(struct bcma_drv_ + { + return bcma_cc_read32(cc, BCMA_CC_GPIOIN) & mask; + } ++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); + } ++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); + } ++EXPORT_SYMBOL_GPL(bcma_chipco_gpio_outen); + + u32 bcma_chipco_gpio_control(struct bcma_drv_cc *cc, u32 mask, u32 value) + { +@@ -102,11 +105,13 @@ u32 bcma_chipco_gpio_intmask(struct bcma + { + return bcma_cc_write32_masked(cc, BCMA_CC_GPIOIRQ, mask, value); + } ++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); + } ++EXPORT_SYMBOL_GPL(bcma_chipco_gpio_polarity); + + #ifdef CONFIG_BCMA_DRIVER_MIPS + void bcma_chipco_serial_init(struct bcma_drv_cc *cc) diff --git a/target/linux/brcm47xx/patches-3.3/052-bcma-complete-workaround-for-BCMA43224.patch b/target/linux/brcm47xx/patches-3.3/052-bcma-complete-workaround-for-BCMA43224.patch new file mode 100644 index 0000000000..7e40fc80b7 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/052-bcma-complete-workaround-for-BCMA43224.patch @@ -0,0 +1,52 @@ +From efe89df0326b777563d197b8cf1c25209a31ceb0 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Sat, 21 Jan 2012 18:47:42 +0100 +Subject: [PATCH 32/34] bcma: complete workaround for BCMA43224 + + +Signed-off-by: Hauke Mehrtens +--- + drivers/bcma/driver_chipcommon_pmu.c | 15 +++++++++++---- + include/linux/bcma/bcma_driver_chipcommon.h | 5 +++++ + 2 files changed, 16 insertions(+), 4 deletions(-) + +--- a/drivers/bcma/driver_chipcommon_pmu.c ++++ b/drivers/bcma/driver_chipcommon_pmu.c +@@ -142,12 +142,19 @@ void bcma_pmu_workarounds(struct bcma_dr + /* BCM4331 workaround is SPROM-related, we put it in sprom.c */ + break; + case 43224: ++ case 43421: ++ /* enable 12 mA drive strenth for 43224 and set chipControl register bit 15 */ + if (bus->chipinfo.rev == 0) { +- pr_err("Workarounds for 43224 rev 0 not fully " +- "implemented\n"); +- bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x00F000F0); ++ bcma_cc_maskset32(cc, BCMA_CC_CHIPCTL, ++ BCMA_CCTRL_43224_GPIO_TOGGLE, ++ BCMA_CCTRL_43224_GPIO_TOGGLE); ++ bcma_chipco_chipctl_maskset(cc, 0, ++ BCMA_CCTRL_43224A0_12MA_LED_DRIVE, ++ BCMA_CCTRL_43224A0_12MA_LED_DRIVE); + } else { +- bcma_chipco_chipctl_maskset(cc, 0, ~0, 0xF0); ++ bcma_chipco_chipctl_maskset(cc, 0, ++ BCMA_CCTRL_43224B0_12MA_LED_DRIVE, ++ BCMA_CCTRL_43224B0_12MA_LED_DRIVE); + } + break; + case 43225: +--- a/include/linux/bcma/bcma_driver_chipcommon.h ++++ b/include/linux/bcma/bcma_driver_chipcommon.h +@@ -382,6 +382,11 @@ + #define BCMA_CHIPCTL_4331_BT_SHD0_ON_GPIO4 BIT(16) /* enable bt_shd0 at gpio4 */ + #define BCMA_CHIPCTL_4331_BT_SHD1_ON_GPIO5 BIT(17) /* enable bt_shd1 at gpio5 */ + ++/* 43224 chip-specific ChipControl register bits */ ++#define BCMA_CCTRL_43224_GPIO_TOGGLE 0x8000 /* gpio[3:0] pins as btcoex or s/w gpio */ ++#define BCMA_CCTRL_43224A0_12MA_LED_DRIVE 0x00F000F0 /* 12 mA drive strength */ ++#define BCMA_CCTRL_43224B0_12MA_LED_DRIVE 0xF0 /* 12 mA drive strength for later 43224s */ ++ + #define BCMA_FLASH2 0x1c000000 /* Flash Region 2 (region 1 shadowed here) */ + #define BCMA_FLASH2_SZ 0x02000000 /* Size of Flash Region 2 */ + #define BCMA_FLASH1 0x1fc00000 /* MIPS Flash Region 1 */ diff --git a/target/linux/brcm47xx/patches-3.3/060-bcma-use-fallback-sprom-if-no-on-chip-sprom-is-avail.patch b/target/linux/brcm47xx/patches-3.3/060-bcma-use-fallback-sprom-if-no-on-chip-sprom-is-avail.patch new file mode 100644 index 0000000000..abc638d244 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/060-bcma-use-fallback-sprom-if-no-on-chip-sprom-is-avail.patch @@ -0,0 +1,24 @@ +--- a/drivers/bcma/sprom.c ++++ b/drivers/bcma/sprom.c +@@ -404,16 +404,19 @@ int bcma_sprom_get(struct bcma_bus *bus) + return -EOPNOTSUPP; + + if (!bcma_sprom_ext_available(bus)) { ++ bool sprom_onchip; ++ + /* + * External SPROM takes precedence so check + * on-chip OTP only when no external SPROM + * is present. + */ +- if (bcma_sprom_onchip_available(bus)) { ++ sprom_onchip = bcma_sprom_onchip_available(bus); ++ if (sprom_onchip) { + /* determine offset */ + offset = bcma_sprom_onchip_offset(bus); + } +- if (!offset) { ++ if (!offset || !sprom_onchip) { + /* + * Maybe there is no SPROM on the device? + * Now we ask the arch code if there is some sprom diff --git a/target/linux/brcm47xx/patches-3.3/114-MIPS-BCM47xx-Setup-and-register-serial-early.patch b/target/linux/brcm47xx/patches-3.3/114-MIPS-BCM47xx-Setup-and-register-serial-early.patch new file mode 100644 index 0000000000..45d3686e2a --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/114-MIPS-BCM47xx-Setup-and-register-serial-early.patch @@ -0,0 +1,69 @@ +From 9be402f069cc259ad5795b77567d66c4e7f6bef6 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +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 +--- + arch/mips/bcm47xx/setup.c | 39 ++++++++++++++++++++++++++++++++++++++- + 1 files changed, 38 insertions(+), 1 deletions(-) + +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -33,6 +33,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -320,6 +322,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; +@@ -352,6 +379,10 @@ static void __init bcm47xx_register_ssb( + memcpy(&mcore->serial_ports[1], &port, sizeof(port)); + } + } ++ ++#ifdef CONFIG_SERIAL_8250 ++ bcm47xx_early_serial_setup(mcore); ++#endif + } + + static int __init bcm47xx_register_flash_ssb(void) diff --git a/target/linux/brcm47xx/patches-3.3/116-MIPS-BCM47xx-Remove-CFE-console.patch b/target/linux/brcm47xx/patches-3.3/116-MIPS-BCM47xx-Remove-CFE-console.patch new file mode 100644 index 0000000000..4a84b6b60e --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/116-MIPS-BCM47xx-Remove-CFE-console.patch @@ -0,0 +1,141 @@ +From 5219981646071abb6731634bf47781a53e248764 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +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 +--- + arch/mips/Kconfig | 1 - + arch/mips/bcm47xx/prom.c | 82 +++------------------------------------------ + 2 files changed, 6 insertions(+), 77 deletions(-) + +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -100,7 +100,6 @@ config BCM47XX + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + select GENERIC_GPIO +- select SYS_HAS_EARLY_PRINTK + select CFE + help + Support for BCM47XX based boards +--- a/arch/mips/bcm47xx/prom.c ++++ b/arch/mips/bcm47xx/prom.c +@@ -31,96 +31,28 @@ + #include + #include + +-static int cfe_cons_handle; +- + const char *get_system_type(void) + { + return "Broadcom BCM47XX"; + } + +-void prom_putchar(char c) +-{ +- while (cfe_write(cfe_cons_handle, &c, 1) == 0) +- ; +-} +- +-static __init void prom_init_cfe(void) ++static __init int prom_init_cfe(void) + { + uint32_t cfe_ept; + uint32_t cfe_handle; + uint32_t cfe_eptseal; +- int argc = fw_arg0; +- char **envp = (char **) fw_arg2; +- int *prom_vec = (int *) fw_arg3; +- +- /* +- * Check if a loader was used; if NOT, the 4 arguments are +- * what CFE gives us (handle, 0, EPT and EPTSEAL) +- */ +- if (argc < 0) { +- cfe_handle = (uint32_t)argc; +- cfe_ept = (uint32_t)envp; +- cfe_eptseal = (uint32_t)prom_vec; +- } else { +- if ((int)prom_vec < 0) { +- /* +- * Old loader; all it gives us is the handle, +- * so use the "known" entrypoint and assume +- * the seal. +- */ +- cfe_handle = (uint32_t)prom_vec; +- cfe_ept = 0xBFC00500; +- cfe_eptseal = CFE_EPTSEAL; +- } else { +- /* +- * Newer loaders bundle the handle/ept/eptseal +- * Note: prom_vec is in the loader's useg +- * which is still alive in the TLB. +- */ +- cfe_handle = prom_vec[0]; +- cfe_ept = prom_vec[2]; +- cfe_eptseal = prom_vec[3]; +- } +- } ++ ++ cfe_eptseal = (uint32_t) fw_arg3; ++ cfe_handle = (uint32_t) fw_arg0; ++ cfe_ept = (uint32_t) fw_arg2; + + if (cfe_eptseal != CFE_EPTSEAL) { +- /* too early for panic to do any good */ + printk(KERN_ERR "CFE's entrypoint seal doesn't match."); +- while (1) ; ++ return -1; + } + + cfe_init(cfe_handle, cfe_ept); +-} +- +-static __init void prom_init_console(void) +-{ +- /* Initialize CFE console */ +- cfe_cons_handle = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE); +-} +- +-static __init void prom_init_cmdline(void) +-{ +- static char buf[COMMAND_LINE_SIZE] __initdata; +- +- /* Get the kernel command line from CFE */ +- if (cfe_getenv("LINUX_CMDLINE", buf, COMMAND_LINE_SIZE) >= 0) { +- buf[COMMAND_LINE_SIZE - 1] = 0; +- strcpy(arcs_cmdline, buf); +- } +- +- /* Force a console handover by adding a console= argument if needed, +- * as CFE is not available anymore later in the boot process. */ +- if ((strstr(arcs_cmdline, "console=")) == NULL) { +- /* Try to read the default serial port used by CFE */ +- if ((cfe_getenv("BOOT_CONSOLE", buf, COMMAND_LINE_SIZE) < 0) +- || (strncmp("uart", buf, 4))) +- /* Default to uart0 */ +- strcpy(buf, "uart0"); +- +- /* Compute the new command line */ +- snprintf(arcs_cmdline, COMMAND_LINE_SIZE, "%s console=ttyS%c,115200", +- arcs_cmdline, buf[4]); +- } ++ return 0; + } + + static __init void prom_init_mem(void) +@@ -161,8 +93,6 @@ static __init void prom_init_mem(void) + void __init prom_init(void) + { + prom_init_cfe(); +- prom_init_console(); +- prom_init_cmdline(); + prom_init_mem(); + } + diff --git a/target/linux/brcm47xx/patches-3.3/119-fix-boot.patch b/target/linux/brcm47xx/patches-3.3/119-fix-boot.patch new file mode 100644 index 0000000000..ebda0c68f7 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/119-fix-boot.patch @@ -0,0 +1,42 @@ +--- a/arch/mips/kernel/head.S ++++ b/arch/mips/kernel/head.S +@@ -121,14 +121,6 @@ + #endif + .endm + +-#ifndef CONFIG_NO_EXCEPT_FILL +- /* +- * Reserved space for exception handlers. +- * Necessary for machines which link their kernels at KSEG0. +- */ +- .fill 0x400 +-#endif +- + EXPORT(_stext) + + #ifdef CONFIG_BOOT_RAW +@@ -141,6 +133,14 @@ FEXPORT(__kernel_entry) + j kernel_entry + #endif + ++#ifndef CONFIG_NO_EXCEPT_FILL ++ /* ++ * Reserved space for exception handlers. ++ * Necessary for machines which link their kernels at KSEG0. ++ */ ++ .fill 0x400 ++#endif ++ + #ifdef CONFIG_IMAGE_CMDLINE_HACK + .ascii "CMDLINE:" + EXPORT(__image_cmdline) +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -92,6 +92,7 @@ config ATH79 + + config BCM47XX + bool "Broadcom BCM47XX based boards" ++ select BOOT_RAW + select CEVT_R4K + select CSRC_R4K + select DMA_NONCOHERENT diff --git a/target/linux/brcm47xx/patches-3.3/140-bcm47xx-add-gpio_set_debounce.patch b/target/linux/brcm47xx/patches-3.3/140-bcm47xx-add-gpio_set_debounce.patch new file mode 100644 index 0000000000..c37bb13d14 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/140-bcm47xx-add-gpio_set_debounce.patch @@ -0,0 +1,12 @@ +--- a/arch/mips/include/asm/mach-bcm47xx/gpio.h ++++ b/arch/mips/include/asm/mach-bcm47xx/gpio.h +@@ -151,5 +151,9 @@ static inline int gpio_polarity(unsigned + return -EINVAL; + } + ++static inline int gpio_set_debounce(unsigned gpio, unsigned debounce) ++{ ++ return -ENOSYS; ++} + + #endif /* __BCM47XX_GPIO_H */ diff --git a/target/linux/brcm47xx/patches-3.3/150-cpu_fixes.patch b/target/linux/brcm47xx/patches-3.3/150-cpu_fixes.patch new file mode 100644 index 0000000000..a367525ea5 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/150-cpu_fixes.patch @@ -0,0 +1,367 @@ +--- a/arch/mips/include/asm/r4kcache.h ++++ b/arch/mips/include/asm/r4kcache.h +@@ -17,6 +17,20 @@ + #include + #include + ++#ifdef CONFIG_BCM47XX ++#include ++#include ++#define BCM4710_DUMMY_RREG() ((void) *((u8 *) KSEG1ADDR(SSB_ENUM_BASE))) ++ ++#define BCM4710_FILL_TLB(addr) (*(volatile unsigned long *)(addr)) ++#define BCM4710_PROTECTED_FILL_TLB(addr) ({ unsigned long x; get_dbe(x, (volatile unsigned long *)(addr)); }) ++#else ++#define BCM4710_DUMMY_RREG() ++ ++#define BCM4710_FILL_TLB(addr) ++#define BCM4710_PROTECTED_FILL_TLB(addr) ++#endif ++ + /* + * This macro return a properly sign-extended address suitable as base address + * for indexed cache operations. Two issues here: +@@ -150,6 +164,7 @@ static inline void flush_icache_line_ind + static inline void flush_dcache_line_indexed(unsigned long addr) + { + __dflush_prologue ++ BCM4710_DUMMY_RREG(); + cache_op(Index_Writeback_Inv_D, addr); + __dflush_epilogue + } +@@ -169,6 +184,7 @@ static inline void flush_icache_line(uns + static inline void flush_dcache_line(unsigned long addr) + { + __dflush_prologue ++ BCM4710_DUMMY_RREG(); + cache_op(Hit_Writeback_Inv_D, addr); + __dflush_epilogue + } +@@ -176,6 +192,7 @@ static inline void flush_dcache_line(uns + static inline void invalidate_dcache_line(unsigned long addr) + { + __dflush_prologue ++ BCM4710_DUMMY_RREG(); + cache_op(Hit_Invalidate_D, addr); + __dflush_epilogue + } +@@ -208,6 +225,7 @@ static inline void flush_scache_line(uns + */ + static inline void protected_flush_icache_line(unsigned long addr) + { ++ BCM4710_DUMMY_RREG(); + protected_cache_op(Hit_Invalidate_I, addr); + } + +@@ -219,6 +237,7 @@ static inline void protected_flush_icach + */ + static inline void protected_writeback_dcache_line(unsigned long addr) + { ++ BCM4710_DUMMY_RREG(); + protected_cache_op(Hit_Writeback_Inv_D, addr); + } + +@@ -339,8 +358,52 @@ static inline void invalidate_tcache_pag + : "r" (base), \ + "i" (op)); + ++static inline void blast_dcache(void) ++{ ++ unsigned long start = KSEG0; ++ unsigned long dcache_size = current_cpu_data.dcache.waysize * current_cpu_data.dcache.ways; ++ unsigned long end = (start + dcache_size); ++ ++ do { ++ BCM4710_DUMMY_RREG(); ++ cache_op(Index_Writeback_Inv_D, start); ++ start += current_cpu_data.dcache.linesz; ++ } while(start < end); ++} ++ ++static inline void blast_dcache_page(unsigned long page) ++{ ++ unsigned long start = page; ++ unsigned long end = start + PAGE_SIZE; ++ ++ BCM4710_FILL_TLB(start); ++ do { ++ BCM4710_DUMMY_RREG(); ++ cache_op(Hit_Writeback_Inv_D, start); ++ start += current_cpu_data.dcache.linesz; ++ } while(start < end); ++} ++ ++static inline void blast_dcache_page_indexed(unsigned long page) ++{ ++ unsigned long start = page; ++ unsigned long end = start + PAGE_SIZE; ++ unsigned long ws_inc = 1UL << current_cpu_data.dcache.waybit; ++ unsigned long ws_end = current_cpu_data.dcache.ways << ++ current_cpu_data.dcache.waybit; ++ unsigned long ws, addr; ++ for (ws = 0; ws < ws_end; ws += ws_inc) { ++ start = page + ws; ++ for (addr = start; addr < end; addr += current_cpu_data.dcache.linesz) { ++ BCM4710_DUMMY_RREG(); ++ cache_op(Index_Writeback_Inv_D, addr); ++ } ++ } ++} ++ ++ + /* build blast_xxx, blast_xxx_page, blast_xxx_page_indexed */ +-#define __BUILD_BLAST_CACHE(pfx, desc, indexop, hitop, lsize) \ ++#define __BUILD_BLAST_CACHE(pfx, desc, indexop, hitop, lsize, war) \ + static inline void blast_##pfx##cache##lsize(void) \ + { \ + unsigned long start = INDEX_BASE; \ +@@ -352,6 +415,7 @@ static inline void blast_##pfx##cache##l + \ + __##pfx##flush_prologue \ + \ ++ war \ + for (ws = 0; ws < ws_end; ws += ws_inc) \ + for (addr = start; addr < end; addr += lsize * 32) \ + cache##lsize##_unroll32(addr|ws, indexop); \ +@@ -366,6 +430,7 @@ static inline void blast_##pfx##cache##l + \ + __##pfx##flush_prologue \ + \ ++ war \ + do { \ + cache##lsize##_unroll32(start, hitop); \ + start += lsize * 32; \ +@@ -384,6 +449,8 @@ static inline void blast_##pfx##cache##l + current_cpu_data.desc.waybit; \ + unsigned long ws, addr; \ + \ ++ war \ ++ \ + __##pfx##flush_prologue \ + \ + for (ws = 0; ws < ws_end; ws += ws_inc) \ +@@ -393,36 +460,38 @@ static inline void blast_##pfx##cache##l + __##pfx##flush_epilogue \ + } + +-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 16) +-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 16) +-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 16) +-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 32) +-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 32) +-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 32) +-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 64) +-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64) +-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 64) +-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 128) +- +-__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 16) +-__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 32) +-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 16) +-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 32) +-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 64) +-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 128) ++__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 16, ) ++__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 16, BCM4710_FILL_TLB(start);) ++__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 16, ) ++__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 32, ) ++__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 32, BCM4710_FILL_TLB(start);) ++__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 32, ) ++__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 64, ) ++__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64, BCM4710_FILL_TLB(start);) ++__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 64, ) ++__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 128, ) ++ ++__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 16, ) ++__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 32, ) ++__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 16, ) ++__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 32, ) ++__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 64, ) ++__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 128, ) + + /* build blast_xxx_range, protected_blast_xxx_range */ +-#define __BUILD_BLAST_CACHE_RANGE(pfx, desc, hitop, prot) \ ++#define __BUILD_BLAST_CACHE_RANGE(pfx, desc, hitop, prot, war, war2) \ + static inline void prot##blast_##pfx##cache##_range(unsigned long start, \ + unsigned long end) \ + { \ + unsigned long lsize = cpu_##desc##_line_size(); \ + unsigned long addr = start & ~(lsize - 1); \ + unsigned long aend = (end - 1) & ~(lsize - 1); \ ++ war \ + \ + __##pfx##flush_prologue \ + \ + while (1) { \ ++ war2 \ + prot##cache_op(hitop, addr); \ + if (addr == aend) \ + break; \ +@@ -432,13 +501,13 @@ static inline void prot##blast_##pfx##ca + __##pfx##flush_epilogue \ + } + +-__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, protected_) +-__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, protected_) +-__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, protected_) +-__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, ) +-__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, ) ++__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, protected_, BCM4710_PROTECTED_FILL_TLB(addr); BCM4710_PROTECTED_FILL_TLB(aend);, BCM4710_DUMMY_RREG();) ++__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, protected_,, ) ++__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, protected_,, ) ++__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D,, BCM4710_FILL_TLB(addr); BCM4710_FILL_TLB(aend);, BCM4710_DUMMY_RREG();) ++__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD,,, ) + /* blast_inv_dcache_range */ +-__BUILD_BLAST_CACHE_RANGE(inv_d, dcache, Hit_Invalidate_D, ) +-__BUILD_BLAST_CACHE_RANGE(inv_s, scache, Hit_Invalidate_SD, ) ++__BUILD_BLAST_CACHE_RANGE(inv_d, dcache, Hit_Invalidate_D,,,BCM4710_DUMMY_RREG();) ++__BUILD_BLAST_CACHE_RANGE(inv_s, scache, Hit_Invalidate_SD,,, ) + + #endif /* _ASM_R4KCACHE_H */ +--- a/arch/mips/include/asm/stackframe.h ++++ b/arch/mips/include/asm/stackframe.h +@@ -449,6 +449,10 @@ + .macro RESTORE_SP_AND_RET + LONG_L sp, PT_R29(sp) + .set mips3 ++#ifdef CONFIG_BCM47XX ++ nop ++ nop ++#endif + eret + .set mips0 + .endm +--- a/arch/mips/kernel/genex.S ++++ b/arch/mips/kernel/genex.S +@@ -52,6 +52,10 @@ NESTED(except_vec1_generic, 0, sp) + NESTED(except_vec3_generic, 0, sp) + .set push + .set noat ++#ifdef CONFIG_BCM47XX ++ nop ++ nop ++#endif + #if R5432_CP0_INTERRUPT_WAR + mfc0 k0, CP0_INDEX + #endif +--- a/arch/mips/mm/c-r4k.c ++++ b/arch/mips/mm/c-r4k.c +@@ -35,6 +35,9 @@ + #include /* for run_uncached() */ + + ++/* For enabling BCM4710 cache workarounds */ ++int bcm4710 = 0; ++ + /* + * Special Variant of smp_call_function for use by cache functions: + * +@@ -111,6 +114,9 @@ static void __cpuinit r4k_blast_dcache_p + { + unsigned long dc_lsize = cpu_dcache_line_size(); + ++ if (bcm4710) ++ r4k_blast_dcache_page = blast_dcache_page; ++ else + if (dc_lsize == 0) + r4k_blast_dcache_page = (void *)cache_noop; + else if (dc_lsize == 16) +@@ -127,6 +133,9 @@ static void __cpuinit r4k_blast_dcache_p + { + unsigned long dc_lsize = cpu_dcache_line_size(); + ++ if (bcm4710) ++ r4k_blast_dcache_page_indexed = blast_dcache_page_indexed; ++ else + if (dc_lsize == 0) + r4k_blast_dcache_page_indexed = (void *)cache_noop; + else if (dc_lsize == 16) +@@ -143,6 +152,9 @@ static void __cpuinit r4k_blast_dcache_s + { + unsigned long dc_lsize = cpu_dcache_line_size(); + ++ if (bcm4710) ++ r4k_blast_dcache = blast_dcache; ++ else + if (dc_lsize == 0) + r4k_blast_dcache = (void *)cache_noop; + else if (dc_lsize == 16) +@@ -683,6 +695,8 @@ static void local_r4k_flush_cache_sigtra + unsigned long addr = (unsigned long) arg; + + R4600_HIT_CACHEOP_WAR_IMPL; ++ BCM4710_PROTECTED_FILL_TLB(addr); ++ BCM4710_PROTECTED_FILL_TLB(addr + 4); + if (dc_lsize) + protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); + if (!cpu_icache_snoops_remote_store && scache_size) +@@ -1349,6 +1363,17 @@ static void __cpuinit coherency_setup(vo + * silly idea of putting something else there ... + */ + switch (current_cpu_type()) { ++ case CPU_BMIPS3300: ++ { ++ u32 cm; ++ cm = read_c0_diag(); ++ /* Enable icache */ ++ cm |= (1 << 31); ++ /* Enable dcache */ ++ cm |= (1 << 30); ++ write_c0_diag(cm); ++ } ++ break; + case CPU_R4000PC: + case CPU_R4000SC: + case CPU_R4000MC: +@@ -1405,6 +1430,15 @@ void __cpuinit r4k_cache_init(void) + break; + } + ++ /* Check if special workarounds are required */ ++#ifdef CONFIG_BCM47XX ++ if (current_cpu_data.cputype == CPU_BMIPS32 && (current_cpu_data.processor_id & 0xff) == 0) { ++ printk("Enabling BCM4710A0 cache workarounds.\n"); ++ bcm4710 = 1; ++ } else ++#endif ++ bcm4710 = 0; ++ + probe_pcache(); + setup_scache(); + +@@ -1465,5 +1499,13 @@ void __cpuinit r4k_cache_init(void) + #if !defined(CONFIG_MIPS_CMP) + local_r4k___flush_cache_all(NULL); + #endif ++#ifdef CONFIG_BCM47XX ++ { ++ static void (*_coherency_setup)(void); ++ _coherency_setup = (void (*)(void)) KSEG1ADDR(coherency_setup); ++ _coherency_setup(); ++ } ++#else + coherency_setup(); ++#endif + } +--- a/arch/mips/mm/tlbex.c ++++ b/arch/mips/mm/tlbex.c +@@ -1264,6 +1264,9 @@ static void __cpuinit build_r4000_tlb_re + /* No need for uasm_i_nop */ + } + ++#ifdef CONFIG_BCM47XX ++ uasm_i_nop(&p); ++#endif + #ifdef CONFIG_64BIT + build_get_pmde64(&p, &l, &r, K0, K1); /* get pmd in K1 */ + #else +@@ -1794,6 +1797,9 @@ build_r4000_tlbchange_handler_head(u32 * + { + struct work_registers wr = build_get_work_registers(p); + ++#ifdef CONFIG_BCM47XX ++ uasm_i_nop(p); ++#endif + #ifdef CONFIG_64BIT + build_get_pmde64(p, l, r, wr.r1, wr.r2); /* get pmd in ptr */ + #else diff --git a/target/linux/brcm47xx/patches-3.3/160-kmap_coherent.patch b/target/linux/brcm47xx/patches-3.3/160-kmap_coherent.patch new file mode 100644 index 0000000000..9253e915ed --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/160-kmap_coherent.patch @@ -0,0 +1,77 @@ +--- a/arch/mips/include/asm/cpu-features.h ++++ b/arch/mips/include/asm/cpu-features.h +@@ -110,6 +110,9 @@ + #ifndef cpu_has_pindexed_dcache + #define cpu_has_pindexed_dcache (cpu_data[0].dcache.flags & MIPS_CACHE_PINDEX) + #endif ++#ifndef cpu_use_kmap_coherent ++#define cpu_use_kmap_coherent 1 ++#endif + + /* + * I-Cache snoops remote store. This only matters on SMP. Some multiprocessors +--- /dev/null ++++ b/arch/mips/include/asm/mach-bcm47xx/cpu-feature-overrides.h +@@ -0,0 +1,13 @@ ++/* ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org) ++ */ ++#ifndef __ASM_MACH_BCM47XX_CPU_FEATURE_OVERRIDES_H ++#define __ASM_MACH_BCM47XX_CPU_FEATURE_OVERRIDES_H ++ ++#define cpu_use_kmap_coherent 0 ++ ++#endif /* __ASM_MACH_BCM47XX_CPU_FEATURE_OVERRIDES_H */ +--- a/arch/mips/mm/c-r4k.c ++++ b/arch/mips/mm/c-r4k.c +@@ -507,7 +507,7 @@ static inline void local_r4k_flush_cache + */ + map_coherent = (cpu_has_dc_aliases && + page_mapped(page) && !Page_dcache_dirty(page)); +- if (map_coherent) ++ if (map_coherent && cpu_use_kmap_coherent) + vaddr = kmap_coherent(page, addr); + else + vaddr = kmap_atomic(page, KM_USER0); +@@ -530,7 +530,7 @@ static inline void local_r4k_flush_cache + } + + if (vaddr) { +- if (map_coherent) ++ if (map_coherent && cpu_use_kmap_coherent) + kunmap_coherent(); + else + kunmap_atomic(vaddr, KM_USER0); +--- a/arch/mips/mm/init.c ++++ b/arch/mips/mm/init.c +@@ -208,7 +208,7 @@ void copy_user_highpage(struct page *to, + void *vfrom, *vto; + + vto = kmap_atomic(to, KM_USER1); +- if (cpu_has_dc_aliases && ++ if (cpu_has_dc_aliases && cpu_use_kmap_coherent && + page_mapped(from) && !Page_dcache_dirty(from)) { + vfrom = kmap_coherent(from, vaddr); + copy_page(vto, vfrom); +@@ -230,7 +230,7 @@ void copy_to_user_page(struct vm_area_st + struct page *page, unsigned long vaddr, void *dst, const void *src, + unsigned long len) + { +- if (cpu_has_dc_aliases && ++ if (cpu_has_dc_aliases && cpu_use_kmap_coherent && + page_mapped(page) && !Page_dcache_dirty(page)) { + void *vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); + memcpy(vto, src, len); +@@ -248,7 +248,7 @@ void copy_from_user_page(struct vm_area_ + struct page *page, unsigned long vaddr, void *dst, const void *src, + unsigned long len) + { +- if (cpu_has_dc_aliases && ++ if (cpu_has_dc_aliases && cpu_use_kmap_coherent && + page_mapped(page) && !Page_dcache_dirty(page)) { + void *vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); + memcpy(dst, vfrom, len); diff --git a/target/linux/brcm47xx/patches-3.3/180-USB-OHCI-Add-a-generic-platform-device-driver.patch b/target/linux/brcm47xx/patches-3.3/180-USB-OHCI-Add-a-generic-platform-device-driver.patch new file mode 100644 index 0000000000..8ea102f1f9 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/180-USB-OHCI-Add-a-generic-platform-device-driver.patch @@ -0,0 +1,271 @@ +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -393,6 +393,16 @@ config USB_CNS3XXX_OHCI + Enable support for the CNS3XXX SOC's on-chip OHCI controller. + It is needed for low-speed USB 1.0 device support. + ++config USB_OHCI_HCD_PLATFORM ++ bool "Generic OHCI driver for a platform device" ++ depends on USB_OHCI_HCD && EXPERIMENTAL ++ default n ++ ---help--- ++ Adds an OHCI host driver for a generic platform device, which ++ provieds a memory space and an irq. ++ ++ If unsure, say N. ++ + config USB_OHCI_BIG_ENDIAN_DESC + bool + depends on USB_OHCI_HCD +--- a/drivers/usb/host/ohci-hcd.c ++++ b/drivers/usb/host/ohci-hcd.c +@@ -1121,6 +1121,11 @@ MODULE_LICENSE ("GPL"); + #define PLATFORM_DRIVER ohci_xls_driver + #endif + ++#ifdef CONFIG_USB_OHCI_HCD_PLATFORM ++#include "ohci-platform.c" ++#define PLATFORM_DRIVER ohci_platform_driver ++#endif ++ + #if !defined(PCI_DRIVER) && \ + !defined(PLATFORM_DRIVER) && \ + !defined(OMAP1_PLATFORM_DRIVER) && \ +--- /dev/null ++++ b/drivers/usb/host/ohci-platform.c +@@ -0,0 +1,194 @@ ++/* ++ * Generic platform ohci driver ++ * ++ * Copyright 2007 Michael Buesch ++ * Copyright 2011-2012 Hauke Mehrtens ++ * ++ * Derived from the OCHI-SSB driver ++ * Derived from the OHCI-PCI driver ++ * Copyright 1999 Roman Weissgaerber ++ * Copyright 2000-2002 David Brownell ++ * Copyright 1999 Linus Torvalds ++ * Copyright 1999 Gregory P. Smith ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++#include ++#include ++ ++static int ohci_platform_reset(struct usb_hcd *hcd) ++{ ++ struct platform_device *pdev = to_platform_device(hcd->self.controller); ++ struct usb_ohci_pdata *pdata = pdev->dev.platform_data; ++ struct ohci_hcd *ohci = hcd_to_ohci(hcd); ++ int err; ++ ++ if (pdata->big_endian_desc) ++ ohci->flags |= OHCI_QUIRK_BE_DESC; ++ if (pdata->big_endian_mmio) ++ ohci->flags |= OHCI_QUIRK_BE_MMIO; ++ if (pdata->no_big_frame_no) ++ ohci->flags |= OHCI_QUIRK_FRAME_NO; ++ ++ ohci_hcd_init(ohci); ++ err = ohci_init(ohci); ++ ++ return err; ++} ++ ++static int ohci_platform_start(struct usb_hcd *hcd) ++{ ++ struct ohci_hcd *ohci = hcd_to_ohci(hcd); ++ int err; ++ ++ err = ohci_run(ohci); ++ if (err < 0) { ++ ohci_err(ohci, "can't start\n"); ++ ohci_stop(hcd); ++ } ++ ++ return err; ++} ++ ++static const struct hc_driver ohci_platform_hc_driver = { ++ .description = hcd_name, ++ .product_desc = "Generic Platform OHCI Controller", ++ .hcd_priv_size = sizeof(struct ohci_hcd), ++ ++ .irq = ohci_irq, ++ .flags = HCD_MEMORY | HCD_USB11, ++ ++ .reset = ohci_platform_reset, ++ .start = ohci_platform_start, ++ .stop = ohci_stop, ++ .shutdown = ohci_shutdown, ++ ++ .urb_enqueue = ohci_urb_enqueue, ++ .urb_dequeue = ohci_urb_dequeue, ++ .endpoint_disable = ohci_endpoint_disable, ++ ++ .get_frame_number = ohci_get_frame, ++ ++ .hub_status_data = ohci_hub_status_data, ++ .hub_control = ohci_hub_control, ++#ifdef CONFIG_PM ++ .bus_suspend = ohci_bus_suspend, ++ .bus_resume = ohci_bus_resume, ++#endif ++ ++ .start_port_reset = ohci_start_port_reset, ++}; ++ ++static int __devinit ohci_platform_probe(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd; ++ struct resource *res_mem; ++ int irq; ++ int err = -ENOMEM; ++ ++ BUG_ON(!dev->dev.platform_data); ++ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ irq = platform_get_irq(dev, 0); ++ if (irq < 0) { ++ pr_err("no irq provieded"); ++ return irq; ++ } ++ ++ res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); ++ if (!res_mem) { ++ pr_err("no memory recourse provieded"); ++ return -ENXIO; ++ } ++ ++ hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, ++ dev_name(&dev->dev)); ++ if (!hcd) ++ return -ENOMEM; ++ ++ hcd->rsrc_start = res_mem->start; ++ hcd->rsrc_len = resource_size(res_mem); ++ ++ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { ++ pr_err("controller already in use"); ++ err = -EBUSY; ++ goto err_put_hcd; ++ } ++ ++ hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); ++ if (!hcd->regs) ++ goto err_release_region; ++ err = usb_add_hcd(hcd, irq, IRQF_SHARED); ++ if (err) ++ goto err_iounmap; ++ ++ platform_set_drvdata(dev, hcd); ++ ++ return err; ++ ++err_iounmap: ++ iounmap(hcd->regs); ++err_release_region: ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++err_put_hcd: ++ usb_put_hcd(hcd); ++ return err; ++} ++ ++static int __devexit ohci_platform_remove(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(dev); ++ ++ usb_remove_hcd(hcd); ++ iounmap(hcd->regs); ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++ usb_put_hcd(hcd); ++ platform_set_drvdata(dev, NULL); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++ ++static int ohci_platform_suspend(struct device *dev) ++{ ++ return 0; ++} ++ ++static int ohci_platform_resume(struct device *dev) ++{ ++ struct usb_hcd *hcd = dev_get_drvdata(dev); ++ ++ ohci_finish_controller_resume(hcd); ++ return 0; ++} ++ ++#else /* !CONFIG_PM */ ++#define ohci_platform_suspend NULL ++#define ohci_platform_resume NULL ++#endif /* CONFIG_PM */ ++ ++static const struct platform_device_id ohci_platform_table[] = { ++ { "ohci-platform", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(platform, ohci_platform_table); ++ ++static const struct dev_pm_ops ohci_platform_pm_ops = { ++ .suspend = ohci_platform_suspend, ++ .resume = ohci_platform_resume, ++}; ++ ++static struct platform_driver ohci_platform_driver = { ++ .id_table = ohci_platform_table, ++ .probe = ohci_platform_probe, ++ .remove = __devexit_p(ohci_platform_remove), ++ .shutdown = usb_hcd_platform_shutdown, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ohci-platform", ++ .pm = &ohci_platform_pm_ops, ++ } ++}; +--- /dev/null ++++ b/include/linux/usb/ohci_pdriver.h +@@ -0,0 +1,38 @@ ++/* ++ * Copyright (C) 2012 Hauke Mehrtens ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software Foundation, ++ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __USB_CORE_OHCI_PDRIVER_H ++#define __USB_CORE_OHCI_PDRIVER_H ++ ++/** ++ * struct usb_ohci_pdata - platform_data for generic ohci driver ++ * ++ * @big_endian_desc: BE descriptors ++ * @big_endian_mmio: BE registers ++ * @no_big_frame_no: no big endian frame_no shift ++ * ++ * These are general configuration options for the OHCI controller. All of ++ * these options are activating more or less workarounds for some hardware. ++ */ ++struct usb_ohci_pdata { ++ unsigned big_endian_desc:1; ++ unsigned big_endian_mmio:1; ++ unsigned no_big_frame_no:1; ++}; ++ ++#endif /* __USB_CORE_OHCI_PDRIVER_H */ diff --git a/target/linux/brcm47xx/patches-3.3/181-USB-EHCI-Add-a-generic-platform-device-driver.patch b/target/linux/brcm47xx/patches-3.3/181-USB-EHCI-Add-a-generic-platform-device-driver.patch new file mode 100644 index 0000000000..62eb87a8a1 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/181-USB-EHCI-Add-a-generic-platform-device-driver.patch @@ -0,0 +1,283 @@ +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -403,6 +403,16 @@ config USB_OHCI_HCD_PLATFORM + + If unsure, say N. + ++config USB_EHCI_HCD_PLATFORM ++ bool "Generic EHCI driver for a platform device" ++ depends on USB_EHCI_HCD && EXPERIMENTAL ++ default n ++ ---help--- ++ Adds an EHCI host driver for a generic platform device, which ++ provieds a memory space and an irq. ++ ++ If unsure, say N. ++ + config USB_OHCI_BIG_ENDIAN_DESC + bool + depends on USB_OHCI_HCD +--- a/drivers/usb/host/ehci-hcd.c ++++ b/drivers/usb/host/ehci-hcd.c +@@ -1381,6 +1381,11 @@ MODULE_LICENSE ("GPL"); + #define PLATFORM_DRIVER ehci_mv_driver + #endif + ++#ifdef CONFIG_USB_EHCI_HCD_PLATFORM ++#include "ehci-platform.c" ++#define PLATFORM_DRIVER ehci_platform_driver ++#endif ++ + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ + !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ + !defined(XILINX_OF_PLATFORM_DRIVER) +--- /dev/null ++++ b/drivers/usb/host/ehci-platform.c +@@ -0,0 +1,198 @@ ++/* ++ * Generic platform ehci driver ++ * ++ * Copyright 2007 Steven Brown ++ * Copyright 2010-2012 Hauke Mehrtens ++ * ++ * Derived from the ohci-ssb driver ++ * Copyright 2007 Michael Buesch ++ * ++ * Derived from the EHCI-PCI driver ++ * Copyright (c) 2000-2004 by David Brownell ++ * ++ * Derived from the ohci-pci driver ++ * Copyright 1999 Roman Weissgaerber ++ * Copyright 2000-2002 David Brownell ++ * Copyright 1999 Linus Torvalds ++ * Copyright 1999 Gregory P. Smith ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++#include ++#include ++ ++static int ehci_platform_reset(struct usb_hcd *hcd) ++{ ++ struct platform_device *pdev = to_platform_device(hcd->self.controller); ++ struct usb_ehci_pdata *pdata = pdev->dev.platform_data; ++ struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++ int retval; ++ ++ hcd->has_tt = pdata->has_tt; ++ ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug; ++ ehci->big_endian_desc = pdata->big_endian_desc; ++ ehci->big_endian_mmio = pdata->big_endian_mmio; ++ ++ ehci->caps = hcd->regs + pdata->caps_offset; ++ retval = ehci_setup(hcd); ++ if (retval) ++ return retval; ++ ++ if (pdata->port_power_on) ++ ehci_port_power(ehci, 1); ++ if (pdata->port_power_off) ++ ehci_port_power(ehci, 0); ++ ++ return 0; ++} ++ ++static const struct hc_driver ehci_platform_hc_driver = { ++ .description = hcd_name, ++ .product_desc = "Generic Platform EHCI Controller", ++ .hcd_priv_size = sizeof(struct ehci_hcd), ++ ++ .irq = ehci_irq, ++ .flags = HCD_MEMORY | HCD_USB2, ++ ++ .reset = ehci_platform_reset, ++ .start = ehci_run, ++ .stop = ehci_stop, ++ .shutdown = ehci_shutdown, ++ ++ .urb_enqueue = ehci_urb_enqueue, ++ .urb_dequeue = ehci_urb_dequeue, ++ .endpoint_disable = ehci_endpoint_disable, ++ .endpoint_reset = ehci_endpoint_reset, ++ ++ .get_frame_number = ehci_get_frame, ++ ++ .hub_status_data = ehci_hub_status_data, ++ .hub_control = ehci_hub_control, ++#if defined(CONFIG_PM) ++ .bus_suspend = ehci_bus_suspend, ++ .bus_resume = ehci_bus_resume, ++#endif ++ .relinquish_port = ehci_relinquish_port, ++ .port_handed_over = ehci_port_handed_over, ++ ++ .update_device = ehci_update_device, ++ ++ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, ++}; ++ ++static int __devinit ehci_platform_probe(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd; ++ struct resource *res_mem; ++ int irq; ++ int err = -ENOMEM; ++ ++ BUG_ON(!dev->dev.platform_data); ++ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ irq = platform_get_irq(dev, 0); ++ if (irq < 0) { ++ pr_err("no irq provieded"); ++ return irq; ++ } ++ res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); ++ if (!res_mem) { ++ pr_err("no memory recourse provieded"); ++ return -ENXIO; ++ } ++ ++ hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, ++ dev_name(&dev->dev)); ++ if (!hcd) ++ return -ENOMEM; ++ ++ hcd->rsrc_start = res_mem->start; ++ hcd->rsrc_len = resource_size(res_mem); ++ ++ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { ++ pr_err("controller already in use"); ++ err = -EBUSY; ++ goto err_put_hcd; ++ } ++ ++ hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); ++ if (!hcd->regs) ++ goto err_release_region; ++ err = usb_add_hcd(hcd, irq, IRQF_SHARED); ++ if (err) ++ goto err_iounmap; ++ ++ platform_set_drvdata(dev, hcd); ++ ++ return err; ++ ++err_iounmap: ++ iounmap(hcd->regs); ++err_release_region: ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++err_put_hcd: ++ usb_put_hcd(hcd); ++ return err; ++} ++ ++static int __devexit ehci_platform_remove(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(dev); ++ ++ usb_remove_hcd(hcd); ++ iounmap(hcd->regs); ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++ usb_put_hcd(hcd); ++ platform_set_drvdata(dev, NULL); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++ ++static int ehci_platform_suspend(struct device *dev) ++{ ++ struct usb_hcd *hcd = dev_get_drvdata(dev); ++ bool wakeup = device_may_wakeup(dev); ++ ++ ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), wakeup); ++ return 0; ++} ++ ++static int ehci_platform_resume(struct device *dev) ++{ ++ struct usb_hcd *hcd = dev_get_drvdata(dev); ++ ++ ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd)); ++ return 0; ++} ++ ++#else /* !CONFIG_PM */ ++#define ehci_platform_suspend NULL ++#define ehci_platform_resume NULL ++#endif /* CONFIG_PM */ ++ ++static const struct platform_device_id ehci_platform_table[] = { ++ { "ehci-platform", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(platform, ehci_platform_table); ++ ++static const struct dev_pm_ops ehci_platform_pm_ops = { ++ .suspend = ehci_platform_suspend, ++ .resume = ehci_platform_resume, ++}; ++ ++static struct platform_driver ehci_platform_driver = { ++ .id_table = ehci_platform_table, ++ .probe = ehci_platform_probe, ++ .remove = __devexit_p(ehci_platform_remove), ++ .shutdown = usb_hcd_platform_shutdown, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ehci-platform", ++ .pm = &ehci_platform_pm_ops, ++ } ++}; +--- /dev/null ++++ b/include/linux/usb/ehci_pdriver.h +@@ -0,0 +1,46 @@ ++/* ++ * Copyright (C) 2012 Hauke Mehrtens ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software Foundation, ++ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __USB_CORE_EHCI_PDRIVER_H ++#define __USB_CORE_EHCI_PDRIVER_H ++ ++/** ++ * struct usb_ehci_pdata - platform_data for generic ehci driver ++ * ++ * @caps_offset: offset of the EHCI Capability Registers to the start of ++ * the io memory region provided to the driver. ++ * @has_tt: set to 1 if TT is integrated in root hub. ++ * @port_power_on: set to 1 if the controller needs a power up after ++ * initialization. ++ * @port_power_off: set to 1 if the controller needs to be powered down ++ * after initialization. ++ * ++ * These are general configuration options for the EHCI controller. All of ++ * these options are activating more or less workarounds for some hardware. ++ */ ++struct usb_ehci_pdata { ++ int caps_offset; ++ unsigned has_tt:1; ++ unsigned has_synopsys_hc_bug:1; ++ unsigned big_endian_desc:1; ++ unsigned big_endian_mmio:1; ++ unsigned port_power_on:1; ++ unsigned port_power_off:1; ++}; ++ ++#endif /* __USB_CORE_EHCI_PDRIVER_H */ diff --git a/target/linux/brcm47xx/patches-3.3/182-bcma-scan-for-extra-address-space.patch b/target/linux/brcm47xx/patches-3.3/182-bcma-scan-for-extra-address-space.patch new file mode 100644 index 0000000000..6e68f0cc8f --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/182-bcma-scan-for-extra-address-space.patch @@ -0,0 +1,45 @@ +--- a/drivers/bcma/scan.c ++++ b/drivers/bcma/scan.c +@@ -297,6 +297,23 @@ static int bcma_get_next_core(struct bcm + return -EILSEQ; + } + ++ /* First Slave Address Descriptor should be port 0: ++ * the main register space for the core ++ */ ++ tmp = bcma_erom_get_addr_desc(bus, eromptr, SCAN_ADDR_TYPE_SLAVE, 0); ++ if (tmp <= 0) { ++ /* Try again to see if it is a bridge */ ++ tmp = bcma_erom_get_addr_desc(bus, eromptr, ++ SCAN_ADDR_TYPE_BRIDGE, 0); ++ if (tmp <= 0) { ++ return -EILSEQ; ++ } else { ++ pr_info("Bridge found\n"); ++ return -ENXIO; ++ } ++ } ++ core->addr = tmp; ++ + /* get & parse slave ports */ + for (i = 0; i < ports[1]; i++) { + for (j = 0; ; j++) { +@@ -309,7 +326,7 @@ static int bcma_get_next_core(struct bcm + break; + } else { + if (i == 0 && j == 0) +- core->addr = tmp; ++ core->addr1 = tmp; + } + } + } +--- a/include/linux/bcma/bcma.h ++++ b/include/linux/bcma/bcma.h +@@ -139,6 +139,7 @@ struct bcma_device { + u8 core_unit; + + u32 addr; ++ u32 addr1; + u32 wrap; + + void __iomem *io_addr; diff --git a/target/linux/brcm47xx/patches-3.3/183-USB-Add-driver-for-the-bcma-bus.patch b/target/linux/brcm47xx/patches-3.3/183-USB-Add-driver-for-the-bcma-bus.patch new file mode 100644 index 0000000000..c021d503af --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/183-USB-Add-driver-for-the-bcma-bus.patch @@ -0,0 +1,362 @@ +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -633,3 +633,15 @@ config USB_PXA168_EHCI + help + Enable support for Marvell PXA168 SoC's on-chip EHCI + host controller ++ ++config USB_HCD_BCMA ++ tristate "BCMA usb host driver" ++ depends on BCMA && EXPERIMENTAL ++ select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD ++ select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD ++ help ++ Enbale support for the EHCI and OCHI host controller on an bcma bus. ++ It converts the bcma driver into two platform device drivers ++ for ehci and ohci. ++ ++ If unsure, say N. +--- a/drivers/usb/host/Makefile ++++ b/drivers/usb/host/Makefile +@@ -37,3 +37,4 @@ obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd + obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o + obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o + obj-$(CONFIG_MIPS_ALCHEMY) += alchemy-common.o ++obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o +--- /dev/null ++++ b/drivers/usb/host/bcma-hcd.c +@@ -0,0 +1,334 @@ ++/* ++ * Broadcom specific Advanced Microcontroller Bus ++ * Broadcom USB-core driver (BCMA bus glue) ++ * ++ * Copyright 2011-2012 Hauke Mehrtens ++ * ++ * Based on ssb-ohci driver ++ * Copyright 2007 Michael Buesch ++ * ++ * Derived from the OHCI-PCI driver ++ * Copyright 1999 Roman Weissgaerber ++ * Copyright 2000-2002 David Brownell ++ * Copyright 1999 Linus Torvalds ++ * Copyright 1999 Gregory P. Smith ++ * ++ * Derived from the USBcore related parts of Broadcom-SB ++ * Copyright 2005-2011 Broadcom Corporation ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("Hauke Mehrtens"); ++MODULE_DESCRIPTION("Common USB driver for BCMA Bus"); ++MODULE_LICENSE("GPL"); ++ ++struct bcma_hcd_device { ++ struct platform_device *ehci_dev; ++ struct platform_device *ohci_dev; ++}; ++ ++/* Wait for bitmask in a register to get set or cleared. ++ * timeout is in units of ten-microseconds. ++ */ ++static int bcma_wait_bits(struct bcma_device *dev, u16 reg, u32 bitmask, ++ int timeout) ++{ ++ int i; ++ u32 val; ++ ++ for (i = 0; i < timeout; i++) { ++ val = bcma_read32(dev, reg); ++ if ((val & bitmask) == bitmask) ++ return 0; ++ udelay(10); ++ } ++ ++ return -ETIMEDOUT; ++} ++ ++static void __devinit bcma_hcd_4716wa(struct bcma_device *dev) ++{ ++#ifdef CONFIG_BCMA_DRIVER_MIPS ++ /* Work around for 4716 failures. */ ++ if (dev->bus->chipinfo.id == 0x4716) { ++ u32 tmp; ++ ++ tmp = bcma_cpu_clock(&dev->bus->drv_mips); ++ if (tmp >= 480000000) ++ tmp = 0x1846b; /* set CDR to 0x11(fast) */ ++ else if (tmp == 453000000) ++ tmp = 0x1046b; /* set CDR to 0x10(slow) */ ++ else ++ tmp = 0; ++ ++ /* Change Shim mdio control reg to fix host not acking at ++ * high frequencies ++ */ ++ if (tmp) { ++ bcma_write32(dev, 0x524, 0x1); /* write sel to enable */ ++ udelay(500); ++ ++ bcma_write32(dev, 0x524, tmp); ++ udelay(500); ++ bcma_write32(dev, 0x524, 0x4ab); ++ udelay(500); ++ bcma_read32(dev, 0x528); ++ bcma_write32(dev, 0x528, 0x80000000); ++ } ++ } ++#endif /* CONFIG_BCMA_DRIVER_MIPS */ ++} ++ ++/* based on arch/mips/brcm-boards/bcm947xx/pcibios.c */ ++static void __devinit bcma_hcd_init_chip(struct bcma_device *dev) ++{ ++ u32 tmp; ++ ++ /* ++ * USB 2.0 special considerations: ++ * ++ * 1. Since the core supports both OHCI and EHCI functions, it must ++ * only be reset once. ++ * ++ * 2. In addition to the standard SI reset sequence, the Host Control ++ * Register must be programmed to bring the USB core and various ++ * phy components out of reset. ++ */ ++ if (!bcma_core_is_enabled(dev)) { ++ bcma_core_enable(dev, 0); ++ mdelay(10); ++ if (dev->id.rev >= 5) { ++ /* Enable Misc PLL */ ++ tmp = bcma_read32(dev, 0x1e0); ++ tmp |= 0x100; ++ bcma_write32(dev, 0x1e0, tmp); ++ if (bcma_wait_bits(dev, 0x1e0, 1 << 24, 100)) ++ printk(KERN_EMERG "Failed to enable misc PPL!\n"); ++ ++ /* Take out of resets */ ++ bcma_write32(dev, 0x200, 0x4ff); ++ udelay(25); ++ bcma_write32(dev, 0x200, 0x6ff); ++ udelay(25); ++ ++ /* Make sure digital and AFE are locked in USB PHY */ ++ bcma_write32(dev, 0x524, 0x6b); ++ udelay(50); ++ tmp = bcma_read32(dev, 0x524); ++ udelay(50); ++ bcma_write32(dev, 0x524, 0xab); ++ udelay(50); ++ tmp = bcma_read32(dev, 0x524); ++ udelay(50); ++ bcma_write32(dev, 0x524, 0x2b); ++ udelay(50); ++ tmp = bcma_read32(dev, 0x524); ++ udelay(50); ++ bcma_write32(dev, 0x524, 0x10ab); ++ udelay(50); ++ tmp = bcma_read32(dev, 0x524); ++ ++ if (bcma_wait_bits(dev, 0x528, 0xc000, 10000)) { ++ tmp = bcma_read32(dev, 0x528); ++ printk(KERN_EMERG ++ "USB20H mdio_rddata 0x%08x\n", tmp); ++ } ++ bcma_write32(dev, 0x528, 0x80000000); ++ tmp = bcma_read32(dev, 0x314); ++ udelay(265); ++ bcma_write32(dev, 0x200, 0x7ff); ++ udelay(10); ++ ++ /* Take USB and HSIC out of non-driving modes */ ++ bcma_write32(dev, 0x510, 0); ++ } else { ++ bcma_write32(dev, 0x200, 0x7ff); ++ ++ udelay(1); ++ } ++ ++ bcma_hcd_4716wa(dev); ++ } ++} ++ ++static const struct usb_ehci_pdata ehci_pdata = { ++}; ++ ++static const struct usb_ohci_pdata ohci_pdata = { ++}; ++ ++static struct platform_device * __devinit ++bcma_hcd_create_pdev(struct bcma_device *dev, bool ohci, u32 addr) ++{ ++ struct platform_device *hci_dev; ++ struct resource hci_res[2]; ++ int ret = -ENOMEM; ++ ++ memset(hci_res, 0, sizeof(hci_res)); ++ ++ hci_res[0].start = addr; ++ hci_res[0].end = hci_res[0].start + 0x1000 - 1; ++ hci_res[0].flags = IORESOURCE_MEM; ++ ++ hci_res[1].start = dev->irq; ++ hci_res[1].flags = IORESOURCE_IRQ; ++ ++ hci_dev = platform_device_alloc(ohci ? "ohci-platform" : ++ "ehci-platform" , 0); ++ if (!hci_dev) ++ return NULL; ++ ++ hci_dev->dev.parent = &dev->dev; ++ hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask; ++ ++ ret = platform_device_add_resources(hci_dev, hci_res, ++ ARRAY_SIZE(hci_res)); ++ if (ret) ++ goto err_alloc; ++ if (ohci) ++ ret = platform_device_add_data(hci_dev, &ohci_pdata, ++ sizeof(ohci_pdata)); ++ else ++ ret = platform_device_add_data(hci_dev, &ehci_pdata, ++ sizeof(ehci_pdata)); ++ if (ret) ++ goto err_alloc; ++ ret = platform_device_add(hci_dev); ++ if (ret) ++ goto err_alloc; ++ ++ return hci_dev; ++ ++err_alloc: ++ platform_device_put(hci_dev); ++ return ERR_PTR(ret); ++} ++ ++static int __devinit bcma_hcd_probe(struct bcma_device *dev) ++{ ++ int err; ++ u16 chipid_top; ++ u32 ohci_addr; ++ struct bcma_hcd_device *usb_dev; ++ struct bcma_chipinfo *chipinfo; ++ ++ chipinfo = &dev->bus->chipinfo; ++ /* USBcores are only connected on embedded devices. */ ++ chipid_top = (chipinfo->id & 0xFF00); ++ if (chipid_top != 0x4700 && chipid_top != 0x5300) ++ return -ENODEV; ++ ++ /* TODO: Probably need checks here; is the core connected? */ ++ ++ if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) || ++ dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32))) ++ return -EOPNOTSUPP; ++ ++ usb_dev = kzalloc(sizeof(struct bcma_hcd_device), GFP_KERNEL); ++ if (!usb_dev) ++ return -ENOMEM; ++ ++ bcma_hcd_init_chip(dev); ++ ++ /* In AI chips EHCI is addrspace 0, OHCI is 1 */ ++ ohci_addr = dev->addr1; ++ if ((chipinfo->id == 0x5357 || chipinfo->id == 0x4749) ++ && chipinfo->rev == 0) ++ ohci_addr = 0x18009000; ++ ++ usb_dev->ohci_dev = bcma_hcd_create_pdev(dev, true, ohci_addr); ++ if (IS_ERR(usb_dev->ohci_dev)) { ++ err = PTR_ERR(usb_dev->ohci_dev); ++ goto err_free_usb_dev; ++ } ++ ++ usb_dev->ehci_dev = bcma_hcd_create_pdev(dev, false, dev->addr); ++ if (IS_ERR(usb_dev->ehci_dev)) { ++ err = PTR_ERR(usb_dev->ehci_dev); ++ goto err_unregister_ohci_dev; ++ } ++ ++ bcma_set_drvdata(dev, usb_dev); ++ return 0; ++ ++err_unregister_ohci_dev: ++ platform_device_unregister(usb_dev->ohci_dev); ++err_free_usb_dev: ++ kfree(usb_dev); ++ return err; ++} ++ ++static void __devexit bcma_hcd_remove(struct bcma_device *dev) ++{ ++ struct bcma_hcd_device *usb_dev = bcma_get_drvdata(dev); ++ struct platform_device *ohci_dev = usb_dev->ohci_dev; ++ struct platform_device *ehci_dev = usb_dev->ehci_dev; ++ ++ if (ohci_dev) ++ platform_device_unregister(ohci_dev); ++ if (ehci_dev) ++ platform_device_unregister(ehci_dev); ++ ++ bcma_core_disable(dev, 0); ++} ++ ++static void bcma_hcd_shutdown(struct bcma_device *dev) ++{ ++ bcma_core_disable(dev, 0); ++} ++ ++#ifdef CONFIG_PM ++ ++static int bcma_hcd_suspend(struct bcma_device *dev, pm_message_t state) ++{ ++ bcma_core_disable(dev, 0); ++ ++ return 0; ++} ++ ++static int bcma_hcd_resume(struct bcma_device *dev) ++{ ++ bcma_core_enable(dev, 0); ++ ++ return 0; ++} ++ ++#else /* !CONFIG_PM */ ++#define bcma_hcd_suspend NULL ++#define bcma_hcd_resume NULL ++#endif /* CONFIG_PM */ ++ ++static const struct bcma_device_id bcma_hcd_table[] __devinitconst = { ++ BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_USB20_HOST, BCMA_ANY_REV, BCMA_ANY_CLASS), ++ BCMA_CORETABLE_END ++}; ++MODULE_DEVICE_TABLE(bcma, bcma_hcd_table); ++ ++static struct bcma_driver bcma_hcd_driver = { ++ .name = KBUILD_MODNAME, ++ .id_table = bcma_hcd_table, ++ .probe = bcma_hcd_probe, ++ .remove = __devexit_p(bcma_hcd_remove), ++ .shutdown = bcma_hcd_shutdown, ++ .suspend = bcma_hcd_suspend, ++ .resume = bcma_hcd_resume, ++}; ++ ++static int __init bcma_hcd_init(void) ++{ ++ return bcma_driver_register(&bcma_hcd_driver); ++} ++module_init(bcma_hcd_init); ++ ++static void __exit bcma_hcd_exit(void) ++{ ++ bcma_driver_unregister(&bcma_hcd_driver); ++} ++module_exit(bcma_hcd_exit); diff --git a/target/linux/brcm47xx/patches-3.3/184-USB-Add-driver-for-the-ssb-bus.patch b/target/linux/brcm47xx/patches-3.3/184-USB-Add-driver-for-the-ssb-bus.patch new file mode 100644 index 0000000000..ecbed777bc --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/184-USB-Add-driver-for-the-ssb-bus.patch @@ -0,0 +1,308 @@ + +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -645,3 +645,15 @@ config USB_HCD_BCMA + for ehci and ohci. + + If unsure, say N. ++ ++config USB_HCD_SSB ++ tristate "SSB usb host driver" ++ depends on SSB && EXPERIMENTAL ++ select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD ++ select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD ++ help ++ Enbale support for the EHCI and OCHI host controller on an bcma bus. ++ It converts the bcma driver into two platform device drivers ++ for ehci and ohci. ++ ++ If unsure, say N. +--- a/drivers/usb/host/Makefile ++++ b/drivers/usb/host/Makefile +@@ -38,3 +38,4 @@ obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-m + obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o + obj-$(CONFIG_MIPS_ALCHEMY) += alchemy-common.o + obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o ++obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o +--- /dev/null ++++ b/drivers/usb/host/ssb-hcd.c +@@ -0,0 +1,279 @@ ++/* ++ * Sonics Silicon Backplane ++ * Broadcom USB-core driver (SSB bus glue) ++ * ++ * Copyright 2011-2012 Hauke Mehrtens ++ * ++ * Based on ssb-ohci driver ++ * Copyright 2007 Michael Buesch ++ * ++ * Derived from the OHCI-PCI driver ++ * Copyright 1999 Roman Weissgaerber ++ * Copyright 2000-2002 David Brownell ++ * Copyright 1999 Linus Torvalds ++ * Copyright 1999 Gregory P. Smith ++ * ++ * Derived from the USBcore related parts of Broadcom-SB ++ * Copyright 2005-2011 Broadcom Corporation ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("Hauke Mehrtens"); ++MODULE_DESCRIPTION("Common USB driver for SSB Bus"); ++MODULE_LICENSE("GPL"); ++ ++#define SSB_HCD_TMSLOW_HOSTMODE (1 << 29) ++ ++struct ssb_hcd_device { ++ struct platform_device *ehci_dev; ++ struct platform_device *ohci_dev; ++ ++ u32 enable_flags; ++}; ++ ++static void __devinit ssb_hcd_5354wa(struct ssb_device *dev) ++{ ++#ifdef CONFIG_SSB_DRIVER_MIPS ++ /* Work around for 5354 failures */ ++ if (dev->id.revision == 2 && dev->bus->chip_id == 0x5354) { ++ /* Change syn01 reg */ ++ ssb_write32(dev, 0x894, 0x00fe00fe); ++ ++ /* Change syn03 reg */ ++ ssb_write32(dev, 0x89c, ssb_read32(dev, 0x89c) | 0x1); ++ } ++#endif ++} ++ ++static void __devinit ssb_hcd_usb20wa(struct ssb_device *dev) ++{ ++ if (dev->id.coreid == SSB_DEV_USB20_HOST) { ++ /* ++ * USB 2.0 special considerations: ++ * ++ * In addition to the standard SSB reset sequence, the Host ++ * Control Register must be programmed to bring the USB core ++ * and various phy components out of reset. ++ */ ++ ssb_write32(dev, 0x200, 0x7ff); ++ ++ /* Change Flush control reg */ ++ ssb_write32(dev, 0x400, ssb_read32(dev, 0x400) & ~8); ++ ssb_read32(dev, 0x400); ++ ++ /* Change Shim control reg */ ++ ssb_write32(dev, 0x304, ssb_read32(dev, 0x304) & ~0x100); ++ ssb_read32(dev, 0x304); ++ ++ udelay(1); ++ ++ ssb_hcd_5354wa(dev); ++ } ++} ++ ++/* based on arch/mips/brcm-boards/bcm947xx/pcibios.c */ ++static u32 __devinit ssb_hcd_init_chip(struct ssb_device *dev) ++{ ++ u32 flags = 0; ++ ++ if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) ++ /* Put the device into host-mode. */ ++ flags |= SSB_HCD_TMSLOW_HOSTMODE; ++ ++ ssb_device_enable(dev, flags); ++ ++ ssb_hcd_usb20wa(dev); ++ ++ return flags; ++} ++ ++static const struct usb_ehci_pdata ehci_pdata = { ++}; ++ ++static const struct usb_ohci_pdata ohci_pdata = { ++}; ++ ++static struct platform_device * __devinit ++ssb_hcd_create_pdev(struct ssb_device *dev, bool ohci, u32 addr, u32 len) ++{ ++ struct platform_device *hci_dev; ++ struct resource hci_res[2]; ++ int ret = -ENOMEM; ++ ++ memset(hci_res, 0, sizeof(hci_res)); ++ ++ hci_res[0].start = addr; ++ hci_res[0].end = hci_res[0].start + len - 1; ++ hci_res[0].flags = IORESOURCE_MEM; ++ ++ hci_res[1].start = dev->irq; ++ hci_res[1].flags = IORESOURCE_IRQ; ++ ++ hci_dev = platform_device_alloc(ohci ? "ohci-platform" : ++ "ehci-platform" , 0); ++ if (!hci_dev) ++ return NULL; ++ ++ hci_dev->dev.parent = dev->dev; ++ hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask; ++ ++ ret = platform_device_add_resources(hci_dev, hci_res, ++ ARRAY_SIZE(hci_res)); ++ if (ret) ++ goto err_alloc; ++ if (ohci) ++ ret = platform_device_add_data(hci_dev, &ohci_pdata, ++ sizeof(ohci_pdata)); ++ else ++ ret = platform_device_add_data(hci_dev, &ehci_pdata, ++ sizeof(ehci_pdata)); ++ if (ret) ++ goto err_alloc; ++ ret = platform_device_add(hci_dev); ++ if (ret) ++ goto err_alloc; ++ ++ return hci_dev; ++ ++err_alloc: ++ platform_device_put(hci_dev); ++ return ERR_PTR(ret); ++} ++ ++static int __devinit ssb_hcd_probe(struct ssb_device *dev, ++ const struct ssb_device_id *id) ++{ ++ int err, tmp; ++ int start, len; ++ u16 chipid_top; ++ u16 coreid = dev->id.coreid; ++ struct ssb_hcd_device *usb_dev; ++ ++ /* USBcores are only connected on embedded devices. */ ++ chipid_top = (dev->bus->chip_id & 0xFF00); ++ if (chipid_top != 0x4700 && chipid_top != 0x5300) ++ return -ENODEV; ++ ++ /* TODO: Probably need checks here; is the core connected? */ ++ ++ if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) || ++ dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32))) ++ return -EOPNOTSUPP; ++ ++ usb_dev = kzalloc(sizeof(struct ssb_hcd_device), GFP_KERNEL); ++ if (!usb_dev) ++ return -ENOMEM; ++ ++ /* We currently always attach SSB_DEV_USB11_HOSTDEV ++ * as HOST OHCI. If we want to attach it as Client device, ++ * we must branch here and call into the (yet to ++ * be written) Client mode driver. Same for remove(). */ ++ usb_dev->enable_flags = ssb_hcd_init_chip(dev); ++ ++ tmp = ssb_read32(dev, SSB_ADMATCH0); ++ ++ start = ssb_admatch_base(tmp); ++ len = (coreid == SSB_DEV_USB20_HOST) ? 0x800 : ssb_admatch_size(tmp); ++ usb_dev->ohci_dev = ssb_hcd_create_pdev(dev, true, start, len); ++ if (IS_ERR(usb_dev->ohci_dev)) { ++ err = PTR_ERR(usb_dev->ohci_dev); ++ goto err_free_usb_dev; ++ } ++ ++ if (coreid == SSB_DEV_USB20_HOST) { ++ start = ssb_admatch_base(tmp) + 0x800; /* ehci core offset */ ++ usb_dev->ehci_dev = ssb_hcd_create_pdev(dev, false, start, len); ++ if (IS_ERR(usb_dev->ehci_dev)) { ++ err = PTR_ERR(usb_dev->ehci_dev); ++ goto err_unregister_ohci_dev; ++ } ++ } ++ ++ ssb_set_drvdata(dev, usb_dev); ++ return 0; ++ ++err_unregister_ohci_dev: ++ platform_device_unregister(usb_dev->ohci_dev); ++err_free_usb_dev: ++ kfree(usb_dev); ++ return err; ++} ++ ++static void __devexit ssb_hcd_remove(struct ssb_device *dev) ++{ ++ struct ssb_hcd_device *usb_dev = ssb_get_drvdata(dev); ++ struct platform_device *ohci_dev = usb_dev->ohci_dev; ++ struct platform_device *ehci_dev = usb_dev->ehci_dev; ++ ++ if (ohci_dev) ++ platform_device_unregister(ohci_dev); ++ if (ehci_dev) ++ platform_device_unregister(ehci_dev); ++ ++ ssb_device_disable(dev, 0); ++} ++ ++static void __devexit ssb_hcd_shutdown(struct ssb_device *dev) ++{ ++ ssb_device_disable(dev, 0); ++} ++ ++#ifdef CONFIG_PM ++ ++static int ssb_hcd_suspend(struct ssb_device *dev, pm_message_t state) ++{ ++ ssb_device_disable(dev, 0); ++ ++ return 0; ++} ++ ++static int ssb_hcd_resume(struct ssb_device *dev) ++{ ++ struct ssb_hcd_device *usb_dev = ssb_get_drvdata(dev); ++ ++ ssb_device_enable(dev, usb_dev->enable_flags); ++ ++ return 0; ++} ++ ++#else /* !CONFIG_PM */ ++#define ssb_hcd_suspend NULL ++#define ssb_hcd_resume NULL ++#endif /* CONFIG_PM */ ++ ++static const struct ssb_device_id ssb_hcd_table[] __devinitconst = { ++ SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV), ++ SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV), ++ SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV), ++ SSB_DEVTABLE_END ++}; ++MODULE_DEVICE_TABLE(ssb, ssb_hcd_table); ++ ++static struct ssb_driver ssb_hcd_driver = { ++ .name = KBUILD_MODNAME, ++ .id_table = ssb_hcd_table, ++ .probe = ssb_hcd_probe, ++ .remove = __devexit_p(ssb_hcd_remove), ++ .shutdown = ssb_hcd_shutdown, ++ .suspend = ssb_hcd_suspend, ++ .resume = ssb_hcd_resume, ++}; ++ ++static int __init ssb_hcd_init(void) ++{ ++ return ssb_driver_register(&ssb_hcd_driver); ++} ++module_init(ssb_hcd_init); ++ ++static void __exit ssb_hcd_exit(void) ++{ ++ ssb_driver_unregister(&ssb_hcd_driver); ++} ++module_exit(ssb_hcd_exit); diff --git a/target/linux/brcm47xx/patches-3.3/185-USB-OHCI-remove-old-SSB-OHCI-driver.patch b/target/linux/brcm47xx/patches-3.3/185-USB-OHCI-remove-old-SSB-OHCI-driver.patch new file mode 100644 index 0000000000..645d409418 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/185-USB-OHCI-remove-old-SSB-OHCI-driver.patch @@ -0,0 +1,340 @@ +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -361,10 +361,15 @@ config USB_OHCI_HCD_PCI + If unsure, say Y. + + config USB_OHCI_HCD_SSB +- bool "OHCI support for Broadcom SSB OHCI core" ++ bool "OHCI support for Broadcom SSB OHCI core (DEPRECATED)" + depends on USB_OHCI_HCD && (SSB = y || SSB = USB_OHCI_HCD) && EXPERIMENTAL ++ select USB_HCD_SSB ++ select USB_OHCI_HCD_PLATFORM + default n + ---help--- ++ This option is deprecated now and the driver was removed, use ++ USB_HCD_SSB and USB_OHCI_HCD_PLATFORM instead. ++ + Support for the Sonics Silicon Backplane (SSB) attached + Broadcom USB OHCI core. + +--- a/drivers/usb/host/ohci-hcd.c ++++ b/drivers/usb/host/ohci-hcd.c +@@ -1081,11 +1081,6 @@ MODULE_LICENSE ("GPL"); + #define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver + #endif + +-#ifdef CONFIG_USB_OHCI_HCD_SSB +-#include "ohci-ssb.c" +-#define SSB_OHCI_DRIVER ssb_ohci_driver +-#endif +- + #ifdef CONFIG_MFD_SM501 + #include "ohci-sm501.c" + #define SM501_OHCI_DRIVER ohci_hcd_sm501_driver +@@ -1134,8 +1129,7 @@ MODULE_LICENSE ("GPL"); + !defined(SA1111_DRIVER) && \ + !defined(PS3_SYSTEM_BUS_DRIVER) && \ + !defined(SM501_OHCI_DRIVER) && \ +- !defined(TMIO_OHCI_DRIVER) && \ +- !defined(SSB_OHCI_DRIVER) ++ !defined(TMIO_OHCI_DRIVER) + #error "missing bus glue for ohci-hcd" + #endif + +@@ -1201,12 +1195,6 @@ static int __init ohci_hcd_mod_init(void + goto error_pci; + #endif + +-#ifdef SSB_OHCI_DRIVER +- retval = ssb_driver_register(&SSB_OHCI_DRIVER); +- if (retval) +- goto error_ssb; +-#endif +- + #ifdef SM501_OHCI_DRIVER + retval = platform_driver_register(&SM501_OHCI_DRIVER); + if (retval < 0) +@@ -1230,10 +1218,6 @@ static int __init ohci_hcd_mod_init(void + platform_driver_unregister(&SM501_OHCI_DRIVER); + error_sm501: + #endif +-#ifdef SSB_OHCI_DRIVER +- ssb_driver_unregister(&SSB_OHCI_DRIVER); +- error_ssb: +-#endif + #ifdef PCI_DRIVER + pci_unregister_driver(&PCI_DRIVER); + error_pci: +@@ -1281,9 +1265,6 @@ static void __exit ohci_hcd_mod_exit(voi + #ifdef SM501_OHCI_DRIVER + platform_driver_unregister(&SM501_OHCI_DRIVER); + #endif +-#ifdef SSB_OHCI_DRIVER +- ssb_driver_unregister(&SSB_OHCI_DRIVER); +-#endif + #ifdef PCI_DRIVER + pci_unregister_driver(&PCI_DRIVER); + #endif +--- a/drivers/usb/host/ohci-ssb.c ++++ /dev/null +@@ -1,260 +0,0 @@ +-/* +- * Sonics Silicon Backplane +- * Broadcom USB-core OHCI driver +- * +- * Copyright 2007 Michael Buesch +- * +- * Derived from the OHCI-PCI driver +- * Copyright 1999 Roman Weissgaerber +- * Copyright 2000-2002 David Brownell +- * Copyright 1999 Linus Torvalds +- * Copyright 1999 Gregory P. Smith +- * +- * Derived from the USBcore related parts of Broadcom-SB +- * Copyright 2005 Broadcom Corporation +- * +- * Licensed under the GNU/GPL. See COPYING for details. +- */ +-#include +- +- +-#define SSB_OHCI_TMSLOW_HOSTMODE (1 << 29) +- +-struct ssb_ohci_device { +- struct ohci_hcd ohci; /* _must_ be at the beginning. */ +- +- u32 enable_flags; +-}; +- +-static inline +-struct ssb_ohci_device *hcd_to_ssb_ohci(struct usb_hcd *hcd) +-{ +- return (struct ssb_ohci_device *)(hcd->hcd_priv); +-} +- +- +-static int ssb_ohci_reset(struct usb_hcd *hcd) +-{ +- struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); +- struct ohci_hcd *ohci = &ohcidev->ohci; +- int err; +- +- ohci_hcd_init(ohci); +- err = ohci_init(ohci); +- +- return err; +-} +- +-static int ssb_ohci_start(struct usb_hcd *hcd) +-{ +- struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); +- struct ohci_hcd *ohci = &ohcidev->ohci; +- int err; +- +- err = ohci_run(ohci); +- if (err < 0) { +- ohci_err(ohci, "can't start\n"); +- ohci_stop(hcd); +- } +- +- return err; +-} +- +-static const struct hc_driver ssb_ohci_hc_driver = { +- .description = "ssb-usb-ohci", +- .product_desc = "SSB OHCI Controller", +- .hcd_priv_size = sizeof(struct ssb_ohci_device), +- +- .irq = ohci_irq, +- .flags = HCD_MEMORY | HCD_USB11, +- +- .reset = ssb_ohci_reset, +- .start = ssb_ohci_start, +- .stop = ohci_stop, +- .shutdown = ohci_shutdown, +- +- .urb_enqueue = ohci_urb_enqueue, +- .urb_dequeue = ohci_urb_dequeue, +- .endpoint_disable = ohci_endpoint_disable, +- +- .get_frame_number = ohci_get_frame, +- +- .hub_status_data = ohci_hub_status_data, +- .hub_control = ohci_hub_control, +-#ifdef CONFIG_PM +- .bus_suspend = ohci_bus_suspend, +- .bus_resume = ohci_bus_resume, +-#endif +- +- .start_port_reset = ohci_start_port_reset, +-}; +- +-static void ssb_ohci_detach(struct ssb_device *dev) +-{ +- struct usb_hcd *hcd = ssb_get_drvdata(dev); +- +- if (hcd->driver->shutdown) +- hcd->driver->shutdown(hcd); +- usb_remove_hcd(hcd); +- iounmap(hcd->regs); +- release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +- usb_put_hcd(hcd); +- ssb_device_disable(dev, 0); +-} +- +-static int ssb_ohci_attach(struct ssb_device *dev) +-{ +- struct ssb_ohci_device *ohcidev; +- struct usb_hcd *hcd; +- int err = -ENOMEM; +- u32 tmp, flags = 0; +- +- if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) || +- dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32))) +- return -EOPNOTSUPP; +- +- if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) { +- /* Put the device into host-mode. */ +- flags |= SSB_OHCI_TMSLOW_HOSTMODE; +- ssb_device_enable(dev, flags); +- } else if (dev->id.coreid == SSB_DEV_USB20_HOST) { +- /* +- * USB 2.0 special considerations: +- * +- * In addition to the standard SSB reset sequence, the Host +- * Control Register must be programmed to bring the USB core +- * and various phy components out of reset. +- */ +- ssb_device_enable(dev, 0); +- ssb_write32(dev, 0x200, 0x7ff); +- +- /* Change Flush control reg */ +- tmp = ssb_read32(dev, 0x400); +- tmp &= ~8; +- ssb_write32(dev, 0x400, tmp); +- tmp = ssb_read32(dev, 0x400); +- +- /* Change Shim control reg */ +- tmp = ssb_read32(dev, 0x304); +- tmp &= ~0x100; +- ssb_write32(dev, 0x304, tmp); +- tmp = ssb_read32(dev, 0x304); +- +- udelay(1); +- +- /* Work around for 5354 failures */ +- if (dev->id.revision == 2 && dev->bus->chip_id == 0x5354) { +- /* Change syn01 reg */ +- tmp = 0x00fe00fe; +- ssb_write32(dev, 0x894, tmp); +- +- /* Change syn03 reg */ +- tmp = ssb_read32(dev, 0x89c); +- tmp |= 0x1; +- ssb_write32(dev, 0x89c, tmp); +- } +- } else +- ssb_device_enable(dev, 0); +- +- hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev, +- dev_name(dev->dev)); +- if (!hcd) +- goto err_dev_disable; +- ohcidev = hcd_to_ssb_ohci(hcd); +- ohcidev->enable_flags = flags; +- +- tmp = ssb_read32(dev, SSB_ADMATCH0); +- hcd->rsrc_start = ssb_admatch_base(tmp); +- hcd->rsrc_len = ssb_admatch_size(tmp); +- hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); +- if (!hcd->regs) +- goto err_put_hcd; +- err = usb_add_hcd(hcd, dev->irq, IRQF_SHARED); +- if (err) +- goto err_iounmap; +- +- ssb_set_drvdata(dev, hcd); +- +- return err; +- +-err_iounmap: +- iounmap(hcd->regs); +-err_put_hcd: +- usb_put_hcd(hcd); +-err_dev_disable: +- ssb_device_disable(dev, flags); +- return err; +-} +- +-static int ssb_ohci_probe(struct ssb_device *dev, +- const struct ssb_device_id *id) +-{ +- int err; +- u16 chipid_top; +- +- /* USBcores are only connected on embedded devices. */ +- chipid_top = (dev->bus->chip_id & 0xFF00); +- if (chipid_top != 0x4700 && chipid_top != 0x5300) +- return -ENODEV; +- +- /* TODO: Probably need checks here; is the core connected? */ +- +- if (usb_disabled()) +- return -ENODEV; +- +- /* We currently always attach SSB_DEV_USB11_HOSTDEV +- * as HOST OHCI. If we want to attach it as Client device, +- * we must branch here and call into the (yet to +- * be written) Client mode driver. Same for remove(). */ +- +- err = ssb_ohci_attach(dev); +- +- return err; +-} +- +-static void ssb_ohci_remove(struct ssb_device *dev) +-{ +- ssb_ohci_detach(dev); +-} +- +-#ifdef CONFIG_PM +- +-static int ssb_ohci_suspend(struct ssb_device *dev, pm_message_t state) +-{ +- ssb_device_disable(dev, 0); +- +- return 0; +-} +- +-static int ssb_ohci_resume(struct ssb_device *dev) +-{ +- struct usb_hcd *hcd = ssb_get_drvdata(dev); +- struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); +- +- ssb_device_enable(dev, ohcidev->enable_flags); +- +- ohci_finish_controller_resume(hcd); +- return 0; +-} +- +-#else /* !CONFIG_PM */ +-#define ssb_ohci_suspend NULL +-#define ssb_ohci_resume NULL +-#endif /* CONFIG_PM */ +- +-static const struct ssb_device_id ssb_ohci_table[] = { +- SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV), +- SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV), +- SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV), +- SSB_DEVTABLE_END +-}; +-MODULE_DEVICE_TABLE(ssb, ssb_ohci_table); +- +-static struct ssb_driver ssb_ohci_driver = { +- .name = KBUILD_MODNAME, +- .id_table = ssb_ohci_table, +- .probe = ssb_ohci_probe, +- .remove = ssb_ohci_remove, +- .suspend = ssb_ohci_suspend, +- .resume = ssb_ohci_resume, +-}; diff --git a/target/linux/brcm47xx/patches-3.3/197-MIPS-BCM47XX-return-number-of-written-bytes-in-nvram.patch b/target/linux/brcm47xx/patches-3.3/197-MIPS-BCM47XX-return-number-of-written-bytes-in-nvram.patch new file mode 100644 index 0000000000..4c4507c2db --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/197-MIPS-BCM47XX-return-number-of-written-bytes-in-nvram.patch @@ -0,0 +1,13 @@ + +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -263,8 +263,7 @@ int nvram_getenv(char *name, char *val, + value = eq + 1; + if ((eq - var) == strlen(name) && + strncmp(var, name, (eq - var)) == 0) { +- snprintf(val, val_len, "%s", value); +- return 0; ++ return snprintf(val, val_len, "%s", value); + } + } + return NVRAM_ERR_ENVNOTFOUND; diff --git a/target/linux/brcm47xx/patches-3.3/198-MIPS-BCM47XX-fix-signature-of-nvram_parse_macaddr.patch b/target/linux/brcm47xx/patches-3.3/198-MIPS-BCM47XX-fix-signature-of-nvram_parse_macaddr.patch new file mode 100644 index 0000000000..ce23504416 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/198-MIPS-BCM47XX-fix-signature-of-nvram_parse_macaddr.patch @@ -0,0 +1,12 @@ + +--- a/arch/mips/include/asm/mach-bcm47xx/nvram.h ++++ b/arch/mips/include/asm/mach-bcm47xx/nvram.h +@@ -37,7 +37,7 @@ struct nvram_header { + + extern int nvram_getenv(char *name, char *val, size_t val_len); + +-static inline void nvram_parse_macaddr(char *buf, u8 *macaddr) ++static inline void nvram_parse_macaddr(char *buf, u8 macaddr[6]) + { + if (strchr(buf, ':')) + sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &macaddr[0], diff --git a/target/linux/brcm47xx/patches-3.3/199-MIPS-BCM47XX-move-and-extend-sprom-parsing.patch b/target/linux/brcm47xx/patches-3.3/199-MIPS-BCM47XX-move-and-extend-sprom-parsing.patch new file mode 100644 index 0000000000..6ec7bd3858 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/199-MIPS-BCM47XX-move-and-extend-sprom-parsing.patch @@ -0,0 +1,802 @@ +--- a/arch/mips/bcm47xx/Makefile ++++ b/arch/mips/bcm47xx/Makefile +@@ -3,5 +3,5 @@ + # under Linux. + # + +-obj-y += gpio.o irq.o nvram.o prom.o serial.o setup.o time.o bus.o ++obj-y += gpio.o irq.o nvram.o prom.o serial.o setup.o time.o bus.o sprom.o + obj-$(CONFIG_BCM47XX_SSB) += wgt634u.o +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -132,156 +132,7 @@ static void bcm47xx_machine_halt(void) + } + + #ifdef CONFIG_BCM47XX_SSB +-#define READ_FROM_NVRAM(_outvar, name, buf) \ +- if (nvram_getprefix(prefix, name, buf, sizeof(buf)) >= 0)\ +- sprom->_outvar = simple_strtoul(buf, NULL, 0); +- +-#define READ_FROM_NVRAM2(_outvar, name1, name2, buf) \ +- if (nvram_getprefix(prefix, name1, buf, sizeof(buf)) >= 0 || \ +- nvram_getprefix(prefix, name2, buf, sizeof(buf)) >= 0)\ +- sprom->_outvar = simple_strtoul(buf, NULL, 0); +- +-static inline int nvram_getprefix(const char *prefix, char *name, +- char *buf, int len) +-{ +- if (prefix) { +- char key[100]; +- +- snprintf(key, sizeof(key), "%s%s", prefix, name); +- return nvram_getenv(key, buf, len); +- } +- +- return nvram_getenv(name, buf, len); +-} +- +-static u32 nvram_getu32(const char *name, char *buf, int len) +-{ +- int rv; +- char key[100]; +- u16 var0, var1; +- +- snprintf(key, sizeof(key), "%s0", name); +- rv = nvram_getenv(key, buf, len); +- /* return 0 here so this looks like unset */ +- if (rv < 0) +- return 0; +- var0 = simple_strtoul(buf, NULL, 0); +- +- snprintf(key, sizeof(key), "%s1", name); +- rv = nvram_getenv(key, buf, len); +- if (rv < 0) +- return 0; +- var1 = simple_strtoul(buf, NULL, 0); +- return var1 << 16 | var0; +-} +- +-static void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix) +-{ +- char buf[100]; +- u32 boardflags; +- +- memset(sprom, 0, sizeof(struct ssb_sprom)); +- +- sprom->revision = 1; /* Fallback: Old hardware does not define this. */ +- READ_FROM_NVRAM(revision, "sromrev", buf); +- if (nvram_getprefix(prefix, "il0macaddr", buf, sizeof(buf)) >= 0 || +- nvram_getprefix(prefix, "macaddr", buf, sizeof(buf)) >= 0) +- nvram_parse_macaddr(buf, sprom->il0mac); +- if (nvram_getprefix(prefix, "et0macaddr", buf, sizeof(buf)) >= 0) +- nvram_parse_macaddr(buf, sprom->et0mac); +- if (nvram_getprefix(prefix, "et1macaddr", buf, sizeof(buf)) >= 0) +- nvram_parse_macaddr(buf, sprom->et1mac); +- READ_FROM_NVRAM(et0phyaddr, "et0phyaddr", buf); +- READ_FROM_NVRAM(et1phyaddr, "et1phyaddr", buf); +- READ_FROM_NVRAM(et0mdcport, "et0mdcport", buf); +- READ_FROM_NVRAM(et1mdcport, "et1mdcport", buf); +- READ_FROM_NVRAM(board_rev, "boardrev", buf); +- READ_FROM_NVRAM(country_code, "ccode", buf); +- READ_FROM_NVRAM(ant_available_a, "aa5g", buf); +- READ_FROM_NVRAM(ant_available_bg, "aa2g", buf); +- READ_FROM_NVRAM(pa0b0, "pa0b0", buf); +- READ_FROM_NVRAM(pa0b1, "pa0b1", buf); +- READ_FROM_NVRAM(pa0b2, "pa0b2", buf); +- READ_FROM_NVRAM(pa1b0, "pa1b0", buf); +- READ_FROM_NVRAM(pa1b1, "pa1b1", buf); +- READ_FROM_NVRAM(pa1b2, "pa1b2", buf); +- READ_FROM_NVRAM(pa1lob0, "pa1lob0", buf); +- READ_FROM_NVRAM(pa1lob2, "pa1lob1", buf); +- READ_FROM_NVRAM(pa1lob1, "pa1lob2", buf); +- READ_FROM_NVRAM(pa1hib0, "pa1hib0", buf); +- READ_FROM_NVRAM(pa1hib2, "pa1hib1", buf); +- READ_FROM_NVRAM(pa1hib1, "pa1hib2", buf); +- READ_FROM_NVRAM2(gpio0, "ledbh0", "wl0gpio0", buf); +- READ_FROM_NVRAM2(gpio1, "ledbh1", "wl0gpio1", buf); +- READ_FROM_NVRAM2(gpio2, "ledbh2", "wl0gpio2", buf); +- READ_FROM_NVRAM2(gpio3, "ledbh3", "wl0gpio3", buf); +- READ_FROM_NVRAM2(maxpwr_bg, "maxp2ga0", "pa0maxpwr", buf); +- READ_FROM_NVRAM2(maxpwr_al, "maxp5gla0", "pa1lomaxpwr", buf); +- READ_FROM_NVRAM2(maxpwr_a, "maxp5ga0", "pa1maxpwr", buf); +- READ_FROM_NVRAM2(maxpwr_ah, "maxp5gha0", "pa1himaxpwr", buf); +- READ_FROM_NVRAM2(itssi_bg, "itt5ga0", "pa0itssit", buf); +- READ_FROM_NVRAM2(itssi_a, "itt2ga0", "pa1itssit", buf); +- READ_FROM_NVRAM(tri2g, "tri2g", buf); +- READ_FROM_NVRAM(tri5gl, "tri5gl", buf); +- READ_FROM_NVRAM(tri5g, "tri5g", buf); +- READ_FROM_NVRAM(tri5gh, "tri5gh", buf); +- READ_FROM_NVRAM(txpid2g[0], "txpid2ga0", buf); +- READ_FROM_NVRAM(txpid2g[1], "txpid2ga1", buf); +- READ_FROM_NVRAM(txpid2g[2], "txpid2ga2", buf); +- READ_FROM_NVRAM(txpid2g[3], "txpid2ga3", buf); +- READ_FROM_NVRAM(txpid5g[0], "txpid5ga0", buf); +- READ_FROM_NVRAM(txpid5g[1], "txpid5ga1", buf); +- READ_FROM_NVRAM(txpid5g[2], "txpid5ga2", buf); +- READ_FROM_NVRAM(txpid5g[3], "txpid5ga3", buf); +- READ_FROM_NVRAM(txpid5gl[0], "txpid5gla0", buf); +- READ_FROM_NVRAM(txpid5gl[1], "txpid5gla1", buf); +- READ_FROM_NVRAM(txpid5gl[2], "txpid5gla2", buf); +- READ_FROM_NVRAM(txpid5gl[3], "txpid5gla3", buf); +- READ_FROM_NVRAM(txpid5gh[0], "txpid5gha0", buf); +- READ_FROM_NVRAM(txpid5gh[1], "txpid5gha1", buf); +- READ_FROM_NVRAM(txpid5gh[2], "txpid5gha2", buf); +- READ_FROM_NVRAM(txpid5gh[3], "txpid5gha3", buf); +- READ_FROM_NVRAM(rxpo2g, "rxpo2g", buf); +- READ_FROM_NVRAM(rxpo5g, "rxpo5g", buf); +- READ_FROM_NVRAM(rssisav2g, "rssisav2g", buf); +- READ_FROM_NVRAM(rssismc2g, "rssismc2g", buf); +- READ_FROM_NVRAM(rssismf2g, "rssismf2g", buf); +- READ_FROM_NVRAM(bxa2g, "bxa2g", buf); +- READ_FROM_NVRAM(rssisav5g, "rssisav5g", buf); +- READ_FROM_NVRAM(rssismc5g, "rssismc5g", buf); +- READ_FROM_NVRAM(rssismf5g, "rssismf5g", buf); +- READ_FROM_NVRAM(bxa5g, "bxa5g", buf); +- READ_FROM_NVRAM(cck2gpo, "cck2gpo", buf); +- +- sprom->ofdm2gpo = nvram_getu32("ofdm2gpo", buf, sizeof(buf)); +- sprom->ofdm5glpo = nvram_getu32("ofdm5glpo", buf, sizeof(buf)); +- sprom->ofdm5gpo = nvram_getu32("ofdm5gpo", buf, sizeof(buf)); +- sprom->ofdm5ghpo = nvram_getu32("ofdm5ghpo", buf, sizeof(buf)); +- +- READ_FROM_NVRAM(antenna_gain.ghz24.a0, "ag0", buf); +- READ_FROM_NVRAM(antenna_gain.ghz24.a1, "ag1", buf); +- READ_FROM_NVRAM(antenna_gain.ghz24.a2, "ag2", buf); +- READ_FROM_NVRAM(antenna_gain.ghz24.a3, "ag3", buf); +- memcpy(&sprom->antenna_gain.ghz5, &sprom->antenna_gain.ghz24, +- sizeof(sprom->antenna_gain.ghz5)); +- +- if (nvram_getprefix(prefix, "boardflags", buf, sizeof(buf)) >= 0) { +- boardflags = simple_strtoul(buf, NULL, 0); +- if (boardflags) { +- sprom->boardflags_lo = (boardflags & 0x0000FFFFU); +- sprom->boardflags_hi = (boardflags & 0xFFFF0000U) >> 16; +- } +- } +- if (nvram_getprefix(prefix, "boardflags2", buf, sizeof(buf)) >= 0) { +- boardflags = simple_strtoul(buf, NULL, 0); +- if (boardflags) { +- sprom->boardflags2_lo = (boardflags & 0x0000FFFFU); +- sprom->boardflags2_hi = (boardflags & 0xFFFF0000U) >> 16; +- } +- } +-} +- +-int bcm47xx_get_sprom(struct ssb_bus *bus, struct ssb_sprom *out) ++static int bcm47xx_get_sprom(struct ssb_bus *bus, struct ssb_sprom *out) + { + char prefix[10]; + +--- /dev/null ++++ b/arch/mips/bcm47xx/sprom.c +@@ -0,0 +1,620 @@ ++/* ++ * Copyright (C) 2004 Florian Schirmer ++ * Copyright (C) 2006 Felix Fietkau ++ * Copyright (C) 2006 Michael Buesch ++ * Copyright (C) 2010 Waldemar Brodkorb ++ * Copyright (C) 2010-2012 Hauke Mehrtens ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN ++ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ++ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ++ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++ ++static void create_key(const char *prefix, const char *postfix, ++ const char *name, char *buf, int len) ++{ ++ if (prefix && postfix) ++ snprintf(buf, len, "%s%s%s", prefix, name, postfix); ++ else if (prefix) ++ snprintf(buf, len, "%s%s", prefix, name); ++ else if (postfix) ++ snprintf(buf, len, "%s%s", name, postfix); ++ else ++ snprintf(buf, len, "%s", name); ++} ++ ++#define NVRAM_READ_VAL(type) \ ++static void nvram_read_ ## type (const char *prefix, \ ++ const char *postfix, const char *name, \ ++ type *val, type allset) \ ++{ \ ++ char buf[100]; \ ++ char key[40]; \ ++ int err; \ ++ type var; \ ++ \ ++ create_key(prefix, postfix, name, key, sizeof(key)); \ ++ \ ++ err = nvram_getenv(key, buf, sizeof(buf)); \ ++ if (err < 0) \ ++ return; \ ++ err = kstrto ## type (buf, 0, &var); \ ++ if (err) { \ ++ pr_warn("can not parse nvram name %s with value %s" \ ++ " got %i", key, buf, err); \ ++ return; \ ++ } \ ++ if (allset && var == allset) \ ++ return; \ ++ *val = var; \ ++} ++ ++NVRAM_READ_VAL(u8) ++NVRAM_READ_VAL(s8) ++NVRAM_READ_VAL(u16) ++NVRAM_READ_VAL(u32) ++ ++#undef NVRAM_READ_VAL ++ ++static void nvram_read_u32_2(const char *prefix, const char *name, ++ u16 *val_lo, u16 *val_hi) ++{ ++ char buf[100]; ++ char key[40]; ++ int err; ++ u32 val; ++ ++ create_key(prefix, NULL, name, key, sizeof(key)); ++ ++ err = nvram_getenv(key, buf, sizeof(buf)); ++ if (err < 0) ++ return; ++ err = kstrtou32(buf, 0, &val); ++ if (err) { ++ pr_warn("can not parse nvram name %s with value %s got %i", ++ key, buf, err); ++ return; ++ } ++ *val_lo = (val & 0x0000FFFFU); ++ *val_hi = (val & 0xFFFF0000U) >> 16; ++} ++ ++static void nvram_read_leddc(const char *prefix, const char *name, ++ u8 *leddc_on_time, u8 *leddc_off_time) ++{ ++ char buf[100]; ++ char key[40]; ++ int err; ++ u32 val; ++ ++ create_key(prefix, NULL, name, key, sizeof(key)); ++ ++ err = nvram_getenv(key, buf, sizeof(buf)); ++ if (err < 0) ++ return; ++ err = kstrtou32(buf, 0, &val); ++ if (err) { ++ pr_warn("can not parse nvram name %s with value %s got %i", ++ key, buf, err); ++ return; ++ } ++ ++ if (val == 0xffff || val == 0xffffffff) ++ return; ++ ++ *leddc_on_time = val & 0xff; ++ *leddc_off_time = (val >> 16) & 0xff; ++} ++ ++static void nvram_read_macaddr(const char *prefix, const char *name, ++ u8 (*val)[6]) ++{ ++ char buf[100]; ++ char key[40]; ++ int err; ++ ++ create_key(prefix, NULL, name, key, sizeof(key)); ++ ++ err = nvram_getenv(key, buf, sizeof(buf)); ++ if (err < 0) ++ return; ++ nvram_parse_macaddr(buf, *val); ++} ++ ++static void nvram_read_alpha2(const char *prefix, const char *name, ++ char (*val)[2]) ++{ ++ char buf[10]; ++ char key[40]; ++ int err; ++ ++ create_key(prefix, NULL, name, key, sizeof(key)); ++ ++ err = nvram_getenv(key, buf, sizeof(buf)); ++ if (err < 0) ++ return; ++ if (buf[0] == '0') ++ return; ++ if (strlen(buf) > 2) { ++ pr_warn("alpha2 is too long %s", buf); ++ return; ++ } ++ memcpy(val, buf, sizeof(val)); ++} ++ ++static void bcm47xx_fill_sprom_r1234589(struct ssb_sprom *sprom, ++ const char *prefix) ++{ ++ nvram_read_u16(prefix, NULL, "boardrev", &sprom->board_rev, 0); ++ nvram_read_u16(prefix, NULL, "boardnum", &sprom->board_num, 0); ++ nvram_read_u8(prefix, NULL, "ledbh0", &sprom->gpio0, 0xff); ++ nvram_read_u8(prefix, NULL, "ledbh1", &sprom->gpio1, 0xff); ++ nvram_read_u8(prefix, NULL, "ledbh2", &sprom->gpio2, 0xff); ++ nvram_read_u8(prefix, NULL, "ledbh3", &sprom->gpio3, 0xff); ++ nvram_read_u8(prefix, NULL, "aa2g", &sprom->ant_available_bg, 0); ++ nvram_read_u8(prefix, NULL, "aa5g", &sprom->ant_available_a, 0); ++ nvram_read_s8(prefix, NULL, "ag0", &sprom->antenna_gain.a0, 0); ++ nvram_read_s8(prefix, NULL, "ag1", &sprom->antenna_gain.a1, 0); ++ nvram_read_alpha2(prefix, "ccode", &sprom->alpha2); ++} ++ ++static void bcm47xx_fill_sprom_r12389(struct ssb_sprom *sprom, ++ const char *prefix) ++{ ++ nvram_read_u16(prefix, NULL, "pa0b0", &sprom->pa0b0, 0); ++ nvram_read_u16(prefix, NULL, "pa0b1", &sprom->pa0b1, 0); ++ nvram_read_u16(prefix, NULL, "pa0b2", &sprom->pa0b2, 0); ++ nvram_read_u8(prefix, NULL, "pa0itssit", &sprom->itssi_bg, 0); ++ nvram_read_u8(prefix, NULL, "pa0maxpwr", &sprom->maxpwr_bg, 0); ++ nvram_read_u16(prefix, NULL, "pa1b0", &sprom->pa1b0, 0); ++ nvram_read_u16(prefix, NULL, "pa1b1", &sprom->pa1b1, 0); ++ nvram_read_u16(prefix, NULL, "pa1b2", &sprom->pa1b2, 0); ++ nvram_read_u8(prefix, NULL, "pa1itssit", &sprom->itssi_a, 0); ++ nvram_read_u8(prefix, NULL, "pa1maxpwr", &sprom->maxpwr_a, 0); ++} ++ ++static void bcm47xx_fill_sprom_r1(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u16(prefix, NULL, "boardflags", &sprom->boardflags_lo, 0); ++ nvram_read_u8(prefix, NULL, "cc", &sprom->country_code, 0); ++} ++ ++static void bcm47xx_fill_sprom_r2389(struct ssb_sprom *sprom, ++ const char *prefix) ++{ ++ nvram_read_u8(prefix, NULL, "opo", &sprom->opo, 0); ++ nvram_read_u16(prefix, NULL, "pa1lob0", &sprom->pa1lob0, 0); ++ nvram_read_u16(prefix, NULL, "pa1lob1", &sprom->pa1lob1, 0); ++ nvram_read_u16(prefix, NULL, "pa1lob2", &sprom->pa1lob2, 0); ++ nvram_read_u16(prefix, NULL, "pa1hib0", &sprom->pa1hib0, 0); ++ nvram_read_u16(prefix, NULL, "pa1hib1", &sprom->pa1hib1, 0); ++ nvram_read_u16(prefix, NULL, "pa1hib2", &sprom->pa1hib2, 0); ++ nvram_read_u8(prefix, NULL, "pa1lomaxpwr", &sprom->maxpwr_al, 0); ++ nvram_read_u8(prefix, NULL, "pa1himaxpwr", &sprom->maxpwr_ah, 0); ++} ++ ++static void bcm47xx_fill_sprom_r2(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u32_2(prefix, "boardflags", &sprom->boardflags_lo, ++ &sprom->boardflags_hi); ++ nvram_read_u16(prefix, NULL, "boardtype", &sprom->board_type, 0); ++} ++ ++static void bcm47xx_fill_sprom_r389(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u8(prefix, NULL, "bxa2g", &sprom->bxa2g, 0); ++ nvram_read_u8(prefix, NULL, "rssisav2g", &sprom->rssisav2g, 0); ++ nvram_read_u8(prefix, NULL, "rssismc2g", &sprom->rssismc2g, 0); ++ nvram_read_u8(prefix, NULL, "rssismf2g", &sprom->rssismf2g, 0); ++ nvram_read_u8(prefix, NULL, "bxa5g", &sprom->bxa5g, 0); ++ nvram_read_u8(prefix, NULL, "rssisav5g", &sprom->rssisav5g, 0); ++ nvram_read_u8(prefix, NULL, "rssismc5g", &sprom->rssismc5g, 0); ++ nvram_read_u8(prefix, NULL, "rssismf5g", &sprom->rssismf5g, 0); ++ nvram_read_u8(prefix, NULL, "tri2g", &sprom->tri2g, 0); ++ nvram_read_u8(prefix, NULL, "tri5g", &sprom->tri5g, 0); ++ nvram_read_u8(prefix, NULL, "tri5gl", &sprom->tri5gl, 0); ++ nvram_read_u8(prefix, NULL, "tri5gh", &sprom->tri5gh, 0); ++ nvram_read_s8(prefix, NULL, "rxpo2g", &sprom->rxpo2g, 0); ++ nvram_read_s8(prefix, NULL, "rxpo5g", &sprom->rxpo5g, 0); ++} ++ ++static void bcm47xx_fill_sprom_r3(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u32_2(prefix, "boardflags", &sprom->boardflags_lo, ++ &sprom->boardflags_hi); ++ nvram_read_u16(prefix, NULL, "boardtype", &sprom->board_type, 0); ++ nvram_read_u8(prefix, NULL, "regrev", &sprom->regrev, 0); ++ nvram_read_leddc(prefix, "leddc", &sprom->leddc_on_time, ++ &sprom->leddc_off_time); ++} ++ ++static void bcm47xx_fill_sprom_r4589(struct ssb_sprom *sprom, ++ const char *prefix) ++{ ++ nvram_read_u32_2(prefix, "boardflags", &sprom->boardflags_lo, ++ &sprom->boardflags_hi); ++ nvram_read_u32_2(prefix, "boardflags2", &sprom->boardflags2_lo, ++ &sprom->boardflags2_hi); ++ nvram_read_u16(prefix, NULL, "boardtype", &sprom->board_type, 0); ++ nvram_read_u8(prefix, NULL, "regrev", &sprom->regrev, 0); ++ nvram_read_s8(prefix, NULL, "ag2", &sprom->antenna_gain.a2, 0); ++ nvram_read_s8(prefix, NULL, "ag3", &sprom->antenna_gain.a3, 0); ++ nvram_read_u8(prefix, NULL, "txchain", &sprom->txchain, 0xf); ++ nvram_read_u8(prefix, NULL, "rxchain", &sprom->rxchain, 0xf); ++ nvram_read_u8(prefix, NULL, "antswitch", &sprom->antswitch, 0xff); ++ nvram_read_leddc(prefix, "leddc", &sprom->leddc_on_time, ++ &sprom->leddc_off_time); ++} ++ ++static void bcm47xx_fill_sprom_r458(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u16(prefix, NULL, "cck2gpo", &sprom->cck2gpo, 0); ++ nvram_read_u32(prefix, NULL, "ofdm2gpo", &sprom->ofdm2gpo, 0); ++ nvram_read_u32(prefix, NULL, "ofdm5gpo", &sprom->ofdm5gpo, 0); ++ nvram_read_u32(prefix, NULL, "ofdm5glpo", &sprom->ofdm5glpo, 0); ++ nvram_read_u32(prefix, NULL, "ofdm5ghpo", &sprom->ofdm5ghpo, 0); ++ nvram_read_u16(prefix, NULL, "cddpo", &sprom->cddpo, 0); ++ nvram_read_u16(prefix, NULL, "stbcpo", &sprom->stbcpo, 0); ++ nvram_read_u16(prefix, NULL, "bw40po", &sprom->bw40po, 0); ++ nvram_read_u16(prefix, NULL, "bwduppo", &sprom->bwduppo, 0); ++ nvram_read_u16(prefix, NULL, "mcs2gpo0", &sprom->mcs2gpo[0], 0); ++ nvram_read_u16(prefix, NULL, "mcs2gpo1", &sprom->mcs2gpo[1], 0); ++ nvram_read_u16(prefix, NULL, "mcs2gpo2", &sprom->mcs2gpo[2], 0); ++ nvram_read_u16(prefix, NULL, "mcs2gpo3", &sprom->mcs2gpo[3], 0); ++ nvram_read_u16(prefix, NULL, "mcs2gpo4", &sprom->mcs2gpo[4], 0); ++ nvram_read_u16(prefix, NULL, "mcs2gpo5", &sprom->mcs2gpo[5], 0); ++ nvram_read_u16(prefix, NULL, "mcs2gpo6", &sprom->mcs2gpo[6], 0); ++ nvram_read_u16(prefix, NULL, "mcs2gpo7", &sprom->mcs2gpo[7], 0); ++ nvram_read_u16(prefix, NULL, "mcs5gpo0", &sprom->mcs5gpo[0], 0); ++ nvram_read_u16(prefix, NULL, "mcs5gpo1", &sprom->mcs5gpo[1], 0); ++ nvram_read_u16(prefix, NULL, "mcs5gpo2", &sprom->mcs5gpo[2], 0); ++ nvram_read_u16(prefix, NULL, "mcs5gpo3", &sprom->mcs5gpo[3], 0); ++ nvram_read_u16(prefix, NULL, "mcs5gpo4", &sprom->mcs5gpo[4], 0); ++ nvram_read_u16(prefix, NULL, "mcs5gpo5", &sprom->mcs5gpo[5], 0); ++ nvram_read_u16(prefix, NULL, "mcs5gpo6", &sprom->mcs5gpo[6], 0); ++ nvram_read_u16(prefix, NULL, "mcs5gpo7", &sprom->mcs5gpo[7], 0); ++ nvram_read_u16(prefix, NULL, "mcs5glpo0", &sprom->mcs5glpo[0], 0); ++ nvram_read_u16(prefix, NULL, "mcs5glpo1", &sprom->mcs5glpo[1], 0); ++ nvram_read_u16(prefix, NULL, "mcs5glpo2", &sprom->mcs5glpo[2], 0); ++ nvram_read_u16(prefix, NULL, "mcs5glpo3", &sprom->mcs5glpo[3], 0); ++ nvram_read_u16(prefix, NULL, "mcs5glpo4", &sprom->mcs5glpo[4], 0); ++ nvram_read_u16(prefix, NULL, "mcs5glpo5", &sprom->mcs5glpo[5], 0); ++ nvram_read_u16(prefix, NULL, "mcs5glpo6", &sprom->mcs5glpo[6], 0); ++ nvram_read_u16(prefix, NULL, "mcs5glpo7", &sprom->mcs5glpo[7], 0); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo0", &sprom->mcs5ghpo[0], 0); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo1", &sprom->mcs5ghpo[1], 0); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo2", &sprom->mcs5ghpo[2], 0); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo3", &sprom->mcs5ghpo[3], 0); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo4", &sprom->mcs5ghpo[4], 0); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo5", &sprom->mcs5ghpo[5], 0); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo6", &sprom->mcs5ghpo[6], 0); ++ nvram_read_u16(prefix, NULL, "mcs5ghpo7", &sprom->mcs5ghpo[7], 0); ++} ++ ++static void bcm47xx_fill_sprom_r45(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u8(prefix, NULL, "txpid2ga0", &sprom->txpid2g[0], 0); ++ nvram_read_u8(prefix, NULL, "txpid2ga1", &sprom->txpid2g[1], 0); ++ nvram_read_u8(prefix, NULL, "txpid2ga2", &sprom->txpid2g[2], 0); ++ nvram_read_u8(prefix, NULL, "txpid2ga3", &sprom->txpid2g[3], 0); ++ nvram_read_u8(prefix, NULL, "txpid5ga0", &sprom->txpid5g[0], 0); ++ nvram_read_u8(prefix, NULL, "txpid5ga1", &sprom->txpid5g[1], 0); ++ nvram_read_u8(prefix, NULL, "txpid5ga2", &sprom->txpid5g[2], 0); ++ nvram_read_u8(prefix, NULL, "txpid5ga3", &sprom->txpid5g[3], 0); ++ nvram_read_u8(prefix, NULL, "txpid5gla0", &sprom->txpid5gl[0], 0); ++ nvram_read_u8(prefix, NULL, "txpid5gla1", &sprom->txpid5gl[1], 0); ++ nvram_read_u8(prefix, NULL, "txpid5gla2", &sprom->txpid5gl[2], 0); ++ nvram_read_u8(prefix, NULL, "txpid5gla3", &sprom->txpid5gl[3], 0); ++ nvram_read_u8(prefix, NULL, "txpid5gha0", &sprom->txpid5gh[0], 0); ++ nvram_read_u8(prefix, NULL, "txpid5gha1", &sprom->txpid5gh[1], 0); ++ nvram_read_u8(prefix, NULL, "txpid5gha2", &sprom->txpid5gh[2], 0); ++ nvram_read_u8(prefix, NULL, "txpid5gha3", &sprom->txpid5gh[3], 0); ++} ++ ++static void bcm47xx_fill_sprom_r89(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u8(prefix, NULL, "tssipos2g", &sprom->fem.ghz2.tssipos, 0); ++ nvram_read_u8(prefix, NULL, "extpagain2g", ++ &sprom->fem.ghz2.extpa_gain, 0); ++ nvram_read_u8(prefix, NULL, "pdetrange2g", ++ &sprom->fem.ghz2.pdet_range, 0); ++ nvram_read_u8(prefix, NULL, "triso2g", &sprom->fem.ghz2.tr_iso, 0); ++ nvram_read_u8(prefix, NULL, "antswctl2g", &sprom->fem.ghz2.antswlut, 0); ++ nvram_read_u8(prefix, NULL, "tssipos5g", &sprom->fem.ghz5.tssipos, 0); ++ nvram_read_u8(prefix, NULL, "extpagain5g", ++ &sprom->fem.ghz5.extpa_gain, 0); ++ nvram_read_u8(prefix, NULL, "pdetrange5g", ++ &sprom->fem.ghz5.pdet_range, 0); ++ nvram_read_u8(prefix, NULL, "triso5g", &sprom->fem.ghz5.tr_iso, 0); ++ nvram_read_u8(prefix, NULL, "antswctl5g", &sprom->fem.ghz5.antswlut, 0); ++ nvram_read_u8(prefix, NULL, "tempthresh", &sprom->tempthresh, 0); ++ nvram_read_u8(prefix, NULL, "tempoffset", &sprom->tempoffset, 0); ++ nvram_read_u16(prefix, NULL, "rawtempsense", &sprom->rawtempsense, 0); ++ nvram_read_u8(prefix, NULL, "measpower", &sprom->measpower, 0); ++ nvram_read_u8(prefix, NULL, "tempsense_slope", ++ &sprom->tempsense_slope, 0); ++ nvram_read_u8(prefix, NULL, "tempcorrx", &sprom->tempcorrx, 0); ++ nvram_read_u8(prefix, NULL, "tempsense_option", ++ &sprom->tempsense_option, 0); ++ nvram_read_u8(prefix, NULL, "freqoffset_corr", ++ &sprom->freqoffset_corr, 0); ++ nvram_read_u8(prefix, NULL, "iqcal_swp_dis", &sprom->iqcal_swp_dis, 0); ++ nvram_read_u8(prefix, NULL, "hw_iqcal_en", &sprom->hw_iqcal_en, 0); ++ nvram_read_u8(prefix, NULL, "elna2g", &sprom->elna2g, 0); ++ nvram_read_u8(prefix, NULL, "elna5g", &sprom->elna5g, 0); ++ nvram_read_u8(prefix, NULL, "phycal_tempdelta", ++ &sprom->phycal_tempdelta, 0); ++ nvram_read_u8(prefix, NULL, "temps_period", &sprom->temps_period, 0); ++ nvram_read_u8(prefix, NULL, "temps_hysteresis", ++ &sprom->temps_hysteresis, 0); ++ nvram_read_u8(prefix, NULL, "measpower1", &sprom->measpower1, 0); ++ nvram_read_u8(prefix, NULL, "measpower2", &sprom->measpower2, 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr2ga0", ++ &sprom->rxgainerr2ga[0], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr2ga1", ++ &sprom->rxgainerr2ga[1], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr2ga2", ++ &sprom->rxgainerr2ga[2], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gla0", ++ &sprom->rxgainerr5gla[0], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gla1", ++ &sprom->rxgainerr5gla[1], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gla2", ++ &sprom->rxgainerr5gla[2], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gma0", ++ &sprom->rxgainerr5gma[0], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gma1", ++ &sprom->rxgainerr5gma[1], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gma2", ++ &sprom->rxgainerr5gma[2], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gha0", ++ &sprom->rxgainerr5gha[0], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gha1", ++ &sprom->rxgainerr5gha[1], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gha2", ++ &sprom->rxgainerr5gha[2], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gua0", ++ &sprom->rxgainerr5gua[0], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gua1", ++ &sprom->rxgainerr5gua[1], 0); ++ nvram_read_u8(prefix, NULL, "rxgainerr5gua2", ++ &sprom->rxgainerr5gua[2], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl2ga0", &sprom->noiselvl2ga[0], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl2ga1", &sprom->noiselvl2ga[1], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl2ga2", &sprom->noiselvl2ga[2], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gla0", ++ &sprom->noiselvl5gla[0], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gla1", ++ &sprom->noiselvl5gla[1], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gla2", ++ &sprom->noiselvl5gla[2], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gma0", ++ &sprom->noiselvl5gma[0], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gma1", ++ &sprom->noiselvl5gma[1], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gma2", ++ &sprom->noiselvl5gma[2], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gha0", ++ &sprom->noiselvl5gha[0], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gha1", ++ &sprom->noiselvl5gha[1], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gha2", ++ &sprom->noiselvl5gha[2], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gua0", ++ &sprom->noiselvl5gua[0], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gua1", ++ &sprom->noiselvl5gua[1], 0); ++ nvram_read_u8(prefix, NULL, "noiselvl5gua2", ++ &sprom->noiselvl5gua[2], 0); ++ nvram_read_u8(prefix, NULL, "pcieingress_war", ++ &sprom->pcieingress_war, 0); ++} ++ ++static void bcm47xx_fill_sprom_r9(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_u16(prefix, NULL, "cckbw202gpo", &sprom->cckbw202gpo, 0); ++ nvram_read_u16(prefix, NULL, "cckbw20ul2gpo", &sprom->cckbw20ul2gpo, 0); ++ nvram_read_u32(prefix, NULL, "legofdmbw202gpo", ++ &sprom->legofdmbw202gpo, 0); ++ nvram_read_u32(prefix, NULL, "legofdmbw20ul2gpo", ++ &sprom->legofdmbw20ul2gpo, 0); ++ nvram_read_u32(prefix, NULL, "legofdmbw205glpo", ++ &sprom->legofdmbw205glpo, 0); ++ nvram_read_u32(prefix, NULL, "legofdmbw20ul5glpo", ++ &sprom->legofdmbw20ul5glpo, 0); ++ nvram_read_u32(prefix, NULL, "legofdmbw205gmpo", ++ &sprom->legofdmbw205gmpo, 0); ++ nvram_read_u32(prefix, NULL, "legofdmbw20ul5gmpo", ++ &sprom->legofdmbw20ul5gmpo, 0); ++ nvram_read_u32(prefix, NULL, "legofdmbw205ghpo", ++ &sprom->legofdmbw205ghpo, 0); ++ nvram_read_u32(prefix, NULL, "legofdmbw20ul5ghpo", ++ &sprom->legofdmbw20ul5ghpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw202gpo", &sprom->mcsbw202gpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw20ul2gpo", &sprom->mcsbw20ul2gpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw402gpo", &sprom->mcsbw402gpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw205glpo", &sprom->mcsbw205glpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw20ul5glpo", ++ &sprom->mcsbw20ul5glpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw405glpo", &sprom->mcsbw405glpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw205gmpo", &sprom->mcsbw205gmpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw20ul5gmpo", ++ &sprom->mcsbw20ul5gmpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw405gmpo", &sprom->mcsbw405gmpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw205ghpo", &sprom->mcsbw205ghpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw20ul5ghpo", ++ &sprom->mcsbw20ul5ghpo, 0); ++ nvram_read_u32(prefix, NULL, "mcsbw405ghpo", &sprom->mcsbw405ghpo, 0); ++ nvram_read_u16(prefix, NULL, "mcs32po", &sprom->mcs32po, 0); ++ nvram_read_u16(prefix, NULL, "legofdm40duppo", ++ &sprom->legofdm40duppo, 0); ++ nvram_read_u8(prefix, NULL, "sar2g", &sprom->sar2g, 0); ++ nvram_read_u8(prefix, NULL, "sar5g", &sprom->sar5g, 0); ++} ++ ++static void bcm47xx_fill_sprom_path_r4589(struct ssb_sprom *sprom, ++ const char *prefix) ++{ ++ char postfix[2]; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(sprom->core_pwr_info); i++) { ++ struct ssb_sprom_core_pwr_info *pwr_info = &sprom->core_pwr_info[i]; ++ snprintf(postfix, sizeof(postfix), "%i", i); ++ nvram_read_u8(prefix, postfix, "maxp2ga", ++ &pwr_info->maxpwr_2g, 0); ++ nvram_read_u8(prefix, postfix, "itt2ga", ++ &pwr_info->itssi_2g, 0); ++ nvram_read_u8(prefix, postfix, "itt5ga", ++ &pwr_info->itssi_5g, 0); ++ nvram_read_u16(prefix, postfix, "pa2gw0a", ++ &pwr_info->pa_2g[0], 0); ++ nvram_read_u16(prefix, postfix, "pa2gw1a", ++ &pwr_info->pa_2g[1], 0); ++ nvram_read_u16(prefix, postfix, "pa2gw2a", ++ &pwr_info->pa_2g[2], 0); ++ nvram_read_u8(prefix, postfix, "maxp5ga", ++ &pwr_info->maxpwr_5g, 0); ++ nvram_read_u8(prefix, postfix, "maxp5gha", ++ &pwr_info->maxpwr_5gh, 0); ++ nvram_read_u8(prefix, postfix, "maxp5gla", ++ &pwr_info->maxpwr_5gl, 0); ++ nvram_read_u16(prefix, postfix, "pa5gw0a", ++ &pwr_info->pa_5g[0], 0); ++ nvram_read_u16(prefix, postfix, "pa5gw1a", ++ &pwr_info->pa_5g[1], 0); ++ nvram_read_u16(prefix, postfix, "pa5gw2a", ++ &pwr_info->pa_5g[2], 0); ++ nvram_read_u16(prefix, postfix, "pa5glw0a", ++ &pwr_info->pa_5gl[0], 0); ++ nvram_read_u16(prefix, postfix, "pa5glw1a", ++ &pwr_info->pa_5gl[1], 0); ++ nvram_read_u16(prefix, postfix, "pa5glw2a", ++ &pwr_info->pa_5gl[2], 0); ++ nvram_read_u16(prefix, postfix, "pa5ghw0a", ++ &pwr_info->pa_5gh[0], 0); ++ nvram_read_u16(prefix, postfix, "pa5ghw1a", ++ &pwr_info->pa_5gh[1], 0); ++ nvram_read_u16(prefix, postfix, "pa5ghw2a", ++ &pwr_info->pa_5gh[2], 0); ++ } ++} ++ ++static void bcm47xx_fill_sprom_path_r45(struct ssb_sprom *sprom, ++ const char *prefix) ++{ ++ char postfix[2]; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(sprom->core_pwr_info); i++) { ++ struct ssb_sprom_core_pwr_info *pwr_info = &sprom->core_pwr_info[i]; ++ snprintf(postfix, sizeof(postfix), "%i", i); ++ nvram_read_u16(prefix, postfix, "pa2gw3a", ++ &pwr_info->pa_2g[3], 0); ++ nvram_read_u16(prefix, postfix, "pa5gw3a", ++ &pwr_info->pa_5g[3], 0); ++ nvram_read_u16(prefix, postfix, "pa5glw3a", ++ &pwr_info->pa_5gl[3], 0); ++ nvram_read_u16(prefix, postfix, "pa5ghw3a", ++ &pwr_info->pa_5gh[3], 0); ++ } ++} ++ ++void bcm47xx_fill_sprom_ethernet(struct ssb_sprom *sprom, const char *prefix) ++{ ++ nvram_read_macaddr(prefix, "et0macaddr", &sprom->et0mac); ++ nvram_read_u8(prefix, NULL, "et0mdcport", &sprom->et0mdcport, 0); ++ nvram_read_u8(prefix, NULL, "et0phyaddr", &sprom->et0phyaddr, 0); ++ ++ nvram_read_macaddr(prefix, "et1macaddr", &sprom->et1mac); ++ nvram_read_u8(prefix, NULL, "et1mdcport", &sprom->et1mdcport, 0); ++ nvram_read_u8(prefix, NULL, "et1phyaddr", &sprom->et1phyaddr, 0); ++ ++ nvram_read_macaddr(prefix, "macaddr", &sprom->il0mac); ++ nvram_read_macaddr(prefix, "il0macaddr", &sprom->il0mac); ++} ++ ++void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix) ++{ ++ memset(sprom, 0, sizeof(struct ssb_sprom)); ++ ++ bcm47xx_fill_sprom_ethernet(sprom, prefix); ++ ++ nvram_read_u8(prefix, NULL, "sromrev", &sprom->revision, 0); ++ ++ switch (sprom->revision) { ++ case 1: ++ bcm47xx_fill_sprom_r1234589(sprom, prefix); ++ bcm47xx_fill_sprom_r12389(sprom, prefix); ++ bcm47xx_fill_sprom_r1(sprom, prefix); ++ break; ++ case 2: ++ bcm47xx_fill_sprom_r1234589(sprom, prefix); ++ bcm47xx_fill_sprom_r12389(sprom, prefix); ++ bcm47xx_fill_sprom_r2389(sprom, prefix); ++ bcm47xx_fill_sprom_r2(sprom, prefix); ++ break; ++ case 3: ++ bcm47xx_fill_sprom_r1234589(sprom, prefix); ++ bcm47xx_fill_sprom_r12389(sprom, prefix); ++ bcm47xx_fill_sprom_r2389(sprom, prefix); ++ bcm47xx_fill_sprom_r389(sprom, prefix); ++ bcm47xx_fill_sprom_r3(sprom, prefix); ++ break; ++ case 4: ++ case 5: ++ bcm47xx_fill_sprom_r1234589(sprom, prefix); ++ bcm47xx_fill_sprom_r4589(sprom, prefix); ++ bcm47xx_fill_sprom_r458(sprom, prefix); ++ bcm47xx_fill_sprom_r45(sprom, prefix); ++ bcm47xx_fill_sprom_path_r4589(sprom, prefix); ++ bcm47xx_fill_sprom_path_r45(sprom, prefix); ++ break; ++ case 8: ++ bcm47xx_fill_sprom_r1234589(sprom, prefix); ++ bcm47xx_fill_sprom_r12389(sprom, prefix); ++ bcm47xx_fill_sprom_r2389(sprom, prefix); ++ bcm47xx_fill_sprom_r389(sprom, prefix); ++ bcm47xx_fill_sprom_r4589(sprom, prefix); ++ bcm47xx_fill_sprom_r458(sprom, prefix); ++ bcm47xx_fill_sprom_r89(sprom, prefix); ++ bcm47xx_fill_sprom_path_r4589(sprom, prefix); ++ break; ++ case 9: ++ bcm47xx_fill_sprom_r1234589(sprom, prefix); ++ bcm47xx_fill_sprom_r12389(sprom, prefix); ++ bcm47xx_fill_sprom_r2389(sprom, prefix); ++ bcm47xx_fill_sprom_r389(sprom, prefix); ++ bcm47xx_fill_sprom_r4589(sprom, prefix); ++ bcm47xx_fill_sprom_r89(sprom, prefix); ++ bcm47xx_fill_sprom_r9(sprom, prefix); ++ bcm47xx_fill_sprom_path_r4589(sprom, prefix); ++ break; ++ default: ++ pr_warn("Unsupported SPROM revision %d detected. Will extract" ++ " v1\n", sprom->revision); ++ sprom->revision = 1; ++ bcm47xx_fill_sprom_r1234589(sprom, prefix); ++ bcm47xx_fill_sprom_r12389(sprom, prefix); ++ bcm47xx_fill_sprom_r1(sprom, prefix); ++ } ++} +--- a/arch/mips/include/asm/mach-bcm47xx/bcm47xx.h ++++ b/arch/mips/include/asm/mach-bcm47xx/bcm47xx.h +@@ -44,4 +44,7 @@ union bcm47xx_bus { + extern union bcm47xx_bus bcm47xx_bus; + extern enum bcm47xx_bus_type bcm47xx_bus_type; + ++void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix); ++void bcm47xx_fill_sprom_ethernet(struct ssb_sprom *sprom, const char *prefix); ++ + #endif /* __ASM_BCM47XX_H */ diff --git a/target/linux/brcm47xx/patches-3.3/200-MIPS-BCM47XX-provide-sprom-to-bcma-bus.patch b/target/linux/brcm47xx/patches-3.3/200-MIPS-BCM47XX-provide-sprom-to-bcma-bus.patch new file mode 100644 index 0000000000..07586c2d15 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/200-MIPS-BCM47XX-provide-sprom-to-bcma-bus.patch @@ -0,0 +1,80 @@ +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -3,7 +3,7 @@ + * Copyright (C) 2006 Felix Fietkau + * Copyright (C) 2006 Michael Buesch + * Copyright (C) 2010 Waldemar Brodkorb +- * Copyright (C) 2010-2011 Hauke Mehrtens ++ * Copyright (C) 2010-2012 Hauke Mehrtens + * Copyright (C) 2011-2012 Tathagata Das + * + * This program is free software; you can redistribute it and/or modify it +@@ -132,7 +132,7 @@ static void bcm47xx_machine_halt(void) + } + + #ifdef CONFIG_BCM47XX_SSB +-static int bcm47xx_get_sprom(struct ssb_bus *bus, struct ssb_sprom *out) ++static int bcm47xx_get_sprom_ssb(struct ssb_bus *bus, struct ssb_sprom *out) + { + char prefix[10]; + +@@ -149,7 +149,7 @@ static int bcm47xx_get_sprom(struct ssb_ + } + + static int bcm47xx_get_invariants(struct ssb_bus *bus, +- struct ssb_init_invariants *iv) ++ struct ssb_init_invariants *iv) + { + char buf[20]; + +@@ -204,7 +204,7 @@ static void __init bcm47xx_register_ssb( + char buf[100]; + struct ssb_mipscore *mcore; + +- err = ssb_arch_register_fallback_sprom(&bcm47xx_get_sprom); ++ err = ssb_arch_register_fallback_sprom(&bcm47xx_get_sprom_ssb); + if (err) + printk(KERN_WARNING "bcm47xx: someone else already registered" + " a ssb SPROM callback handler (err %d)\n", err); +@@ -256,10 +256,41 @@ static int __init bcm47xx_register_flash + #endif + + #ifdef CONFIG_BCM47XX_BCMA ++static int bcm47xx_get_sprom_bcma(struct bcma_bus *bus, struct ssb_sprom *out) ++{ ++ char prefix[10]; ++ struct bcma_device *core; ++ ++ switch (bus->hosttype) { ++ case BCMA_HOSTTYPE_PCI: ++ snprintf(prefix, sizeof(prefix), "pci/%u/%u/", ++ bus->host_pci->bus->number + 1, ++ PCI_SLOT(bus->host_pci->devfn)); ++ bcm47xx_fill_sprom(out, prefix); ++ return 0; ++ case BCMA_HOSTTYPE_SOC: ++ bcm47xx_fill_sprom_ethernet(out, NULL); ++ core = bcma_find_core(bus, BCMA_CORE_80211); ++ if (core) { ++ snprintf(prefix, sizeof(prefix), "sb/%u/", ++ core->core_index); ++ bcm47xx_fill_sprom(out, prefix); ++ } ++ return 0; ++ default: ++ pr_warn("bcm47xx: unable to fill SPROM for given bustype.\n"); ++ return -EINVAL; ++ } ++} ++ + static void __init bcm47xx_register_bcma(void) + { + int err; + ++ err = bcma_arch_register_fallback_sprom(&bcm47xx_get_sprom_bcma); ++ if (err) ++ pr_warn("bcm47xx: someone else already registered a bcma SPROM callback handler (err %d)\n", err); ++ + err = bcma_host_soc_register(&bcm47xx_bus.bcma); + if (err) + panic("Failed to initialize BCMA bus (err %d)", err); diff --git a/target/linux/brcm47xx/patches-3.3/210-b44_phy_fix.patch b/target/linux/brcm47xx/patches-3.3/210-b44_phy_fix.patch new file mode 100644 index 0000000000..2d0536552a --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/210-b44_phy_fix.patch @@ -0,0 +1,54 @@ +--- a/drivers/net/ethernet/broadcom/b44.c ++++ b/drivers/net/ethernet/broadcom/b44.c +@@ -410,10 +410,34 @@ static void b44_wap54g10_workaround(stru + error: + pr_warning("PHY: cannot reset MII transceiver isolate bit\n"); + } ++ ++static void b44_bcm47xx_workarounds(struct b44 *bp) ++{ ++ char buf[20]; ++ struct ssb_device *sdev = bp->sdev; ++ ++ /* Toshiba WRC-1000, Siemens SE505 v1, Askey RT-210W, RT-220W */ ++ if (sdev->bus->sprom.board_num == 100) { ++ bp->phy_addr = B44_PHY_ADDR_NO_PHY; ++ } else { ++ /* WL-HDD */ ++ if (nvram_getenv("hardware_version", buf, sizeof(buf)) >= 0 && ++ !strncmp(buf, "WL300-", strlen("WL300-"))) { ++ if (sdev->bus->sprom.et0phyaddr == 0 && ++ sdev->bus->sprom.et1phyaddr == 1) ++ bp->phy_addr = B44_PHY_ADDR_NO_PHY; ++ } ++ } ++ return; ++} + #else + static inline void b44_wap54g10_workaround(struct b44 *bp) + { + } ++ ++static inline void b44_bcm47xx_workarounds(struct b44 *bp) ++{ ++} + #endif + + static int b44_setup_phy(struct b44 *bp) +@@ -422,6 +446,7 @@ static int b44_setup_phy(struct b44 *bp) + int err; + + b44_wap54g10_workaround(bp); ++ b44_bcm47xx_workarounds(bp); + + if (bp->phy_addr == B44_PHY_ADDR_NO_PHY) + return 0; +@@ -2088,6 +2113,8 @@ static int __devinit b44_get_invariants( + * valid PHY address. */ + bp->phy_addr &= 0x1F; + ++ b44_bcm47xx_workarounds(bp); ++ + memcpy(bp->dev->dev_addr, addr, 6); + + if (!is_valid_ether_addr(&bp->dev->dev_addr[0])){ diff --git a/target/linux/brcm47xx/patches-3.3/211-b44_timeout_spam.patch b/target/linux/brcm47xx/patches-3.3/211-b44_timeout_spam.patch new file mode 100644 index 0000000000..c2eb3ad90b --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/211-b44_timeout_spam.patch @@ -0,0 +1,15 @@ +--- a/drivers/net/ethernet/broadcom/b44.c ++++ b/drivers/net/ethernet/broadcom/b44.c +@@ -187,10 +187,11 @@ static int b44_wait_bit(struct b44 *bp, + udelay(10); + } + if (i == timeout) { ++#if 0 + if (net_ratelimit()) + netdev_err(bp->dev, "BUG! Timeout waiting for bit %08x of register %lx to %s\n", + bit, reg, clear ? "clear" : "set"); +- ++#endif + return -ENODEV; + } + return 0; diff --git a/target/linux/brcm47xx/patches-3.3/230-bcma-find-name-for-non-brcm.patch b/target/linux/brcm47xx/patches-3.3/230-bcma-find-name-for-non-brcm.patch new file mode 100644 index 0000000000..54addee966 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/230-bcma-find-name-for-non-brcm.patch @@ -0,0 +1,99 @@ +--- a/drivers/bcma/scan.c ++++ b/drivers/bcma/scan.c +@@ -19,7 +19,14 @@ struct bcma_device_id_name { + u16 id; + const char *name; + }; +-struct bcma_device_id_name bcma_device_names[] = { ++ ++static const struct bcma_device_id_name bcma_arm_device_names[] = { ++ { BCMA_CORE_ARM_1176, "ARM 1176" }, ++ { BCMA_CORE_ARM_7TDMI, "ARM 7TDMI" }, ++ { BCMA_CORE_ARM_CM3, "ARM CM3" }, ++}; ++ ++static const struct bcma_device_id_name bcma_bcm_device_names[] = { + { BCMA_CORE_OOB_ROUTER, "OOB Router" }, + { BCMA_CORE_INVALID, "Invalid" }, + { BCMA_CORE_CHIPCOMMON, "ChipCommon" }, +@@ -27,7 +34,6 @@ struct bcma_device_id_name bcma_device_n + { BCMA_CORE_SRAM, "SRAM" }, + { BCMA_CORE_SDRAM, "SDRAM" }, + { BCMA_CORE_PCI, "PCI" }, +- { BCMA_CORE_MIPS, "MIPS" }, + { BCMA_CORE_ETHERNET, "Fast Ethernet" }, + { BCMA_CORE_V90, "V90" }, + { BCMA_CORE_USB11_HOSTDEV, "USB 1.1 Hostdev" }, +@@ -44,7 +50,6 @@ struct bcma_device_id_name bcma_device_n + { BCMA_CORE_PHY_A, "PHY A" }, + { BCMA_CORE_PHY_B, "PHY B" }, + { BCMA_CORE_PHY_G, "PHY G" }, +- { BCMA_CORE_MIPS_3302, "MIPS 3302" }, + { BCMA_CORE_USB11_HOST, "USB 1.1 Host" }, + { BCMA_CORE_USB11_DEV, "USB 1.1 Device" }, + { BCMA_CORE_USB20_HOST, "USB 2.0 Host" }, +@@ -58,15 +63,11 @@ struct bcma_device_id_name bcma_device_n + { BCMA_CORE_PHY_N, "PHY N" }, + { BCMA_CORE_SRAM_CTL, "SRAM Controller" }, + { BCMA_CORE_MINI_MACPHY, "Mini MACPHY" }, +- { BCMA_CORE_ARM_1176, "ARM 1176" }, +- { BCMA_CORE_ARM_7TDMI, "ARM 7TDMI" }, + { BCMA_CORE_PHY_LP, "PHY LP" }, + { BCMA_CORE_PMU, "PMU" }, + { BCMA_CORE_PHY_SSN, "PHY SSN" }, + { BCMA_CORE_SDIO_DEV, "SDIO Device" }, +- { BCMA_CORE_ARM_CM3, "ARM CM3" }, + { BCMA_CORE_PHY_HT, "PHY HT" }, +- { BCMA_CORE_MIPS_74K, "MIPS 74K" }, + { BCMA_CORE_MAC_GBIT, "GBit MAC" }, + { BCMA_CORE_DDR12_MEM_CTL, "DDR1/DDR2 Memory Controller" }, + { BCMA_CORE_PCIE_RC, "PCIe Root Complex" }, +@@ -79,16 +80,41 @@ struct bcma_device_id_name bcma_device_n + { BCMA_CORE_SHIM, "SHIM" }, + { BCMA_CORE_DEFAULT, "Default" }, + }; +-const char *bcma_device_name(struct bcma_device_id *id) ++ ++static const struct bcma_device_id_name bcma_mips_device_names[] = { ++ { BCMA_CORE_MIPS, "MIPS" }, ++ { BCMA_CORE_MIPS_3302, "MIPS 3302" }, ++ { BCMA_CORE_MIPS_74K, "MIPS 74K" }, ++}; ++ ++static const char *bcma_device_name(const struct bcma_device_id *id) + { +- int i; ++ const struct bcma_device_id_name *names; ++ int size, i; + +- if (id->manuf == BCMA_MANUF_BCM) { +- for (i = 0; i < ARRAY_SIZE(bcma_device_names); i++) { +- if (bcma_device_names[i].id == id->id) +- return bcma_device_names[i].name; +- } ++ /* search manufacturer specific names */ ++ switch (id->manuf) { ++ case BCMA_MANUF_ARM: ++ names = bcma_arm_device_names; ++ size = ARRAY_SIZE(bcma_arm_device_names); ++ break; ++ case BCMA_MANUF_BCM: ++ names = bcma_bcm_device_names; ++ size = ARRAY_SIZE(bcma_bcm_device_names); ++ break; ++ case BCMA_MANUF_MIPS: ++ names = bcma_mips_device_names; ++ size = ARRAY_SIZE(bcma_mips_device_names); ++ break; ++ default: ++ return "UNKNOWN"; + } ++ ++ for (i = 0; i < size; i++) { ++ if (names[i].id == id->id) ++ return names[i].name; ++ } ++ + return "UNKNOWN"; + } + diff --git a/target/linux/brcm47xx/patches-3.3/231-bcma_reorder_sprom_fill.patch b/target/linux/brcm47xx/patches-3.3/231-bcma_reorder_sprom_fill.patch new file mode 100644 index 0000000000..f4c2e26507 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/231-bcma_reorder_sprom_fill.patch @@ -0,0 +1,44 @@ +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -137,6 +137,7 @@ static int bcm47xx_get_sprom_ssb(struct + char prefix[10]; + + if (bus->bustype == SSB_BUSTYPE_PCI) { ++ memset(out, 0, sizeof(struct ssb_sprom)); + snprintf(prefix, sizeof(prefix), "pci/%u/%u/", + bus->host_pci->bus->number + 1, + PCI_SLOT(bus->host_pci->devfn)); +@@ -165,6 +166,7 @@ static int bcm47xx_get_invariants(struct + if (nvram_getenv("boardrev", buf, sizeof(buf)) >= 0) + iv->boardinfo.rev = (u16)simple_strtoul(buf, NULL, 0); + ++ memset(&iv->sprom, 0, sizeof(struct ssb_sprom)); + bcm47xx_fill_sprom(&iv->sprom, NULL); + + if (nvram_getenv("cardbus", buf, sizeof(buf)) >= 0) +@@ -263,12 +265,14 @@ static int bcm47xx_get_sprom_bcma(struct + + switch (bus->hosttype) { + case BCMA_HOSTTYPE_PCI: ++ memset(out, 0, sizeof(struct ssb_sprom)); + snprintf(prefix, sizeof(prefix), "pci/%u/%u/", + bus->host_pci->bus->number + 1, + PCI_SLOT(bus->host_pci->devfn)); + bcm47xx_fill_sprom(out, prefix); + return 0; + case BCMA_HOSTTYPE_SOC: ++ memset(out, 0, sizeof(struct ssb_sprom)); + bcm47xx_fill_sprom_ethernet(out, NULL); + core = bcma_find_core(bus, BCMA_CORE_80211); + if (core) { +--- a/arch/mips/bcm47xx/sprom.c ++++ b/arch/mips/bcm47xx/sprom.c +@@ -555,8 +555,6 @@ void bcm47xx_fill_sprom_ethernet(struct + + void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix) + { +- memset(sprom, 0, sizeof(struct ssb_sprom)); +- + bcm47xx_fill_sprom_ethernet(sprom, prefix); + + nvram_read_u8(prefix, NULL, "sromrev", &sprom->revision, 0); diff --git a/target/linux/brcm47xx/patches-3.3/232-bcma_account_for_variable_pci_memory.patch b/target/linux/brcm47xx/patches-3.3/232-bcma_account_for_variable_pci_memory.patch new file mode 100644 index 0000000000..3a2d512dc7 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/232-bcma_account_for_variable_pci_memory.patch @@ -0,0 +1,14 @@ +--- a/drivers/bcma/driver_pci_host.c ++++ b/drivers/bcma/driver_pci_host.c +@@ -491,8 +491,9 @@ void __devinit bcma_core_pci_hostmode_in + /* Ok, ready to run, register it to the system. + * The following needs change, if we want to port hostmode + * to non-MIPS platform. */ +- io_map_base = (unsigned long)ioremap_nocache(BCMA_SOC_PCI_MEM, +- 0x04000000); ++ io_map_base = (unsigned long)ioremap_nocache(pc_host->mem_resource.start, ++ pc_host->mem_resource.end - ++ pc_host->mem_resource.start + 1); + pc_host->pci_controller.io_map_base = io_map_base; + set_io_port_base(pc_host->pci_controller.io_map_base); + /* Give some time to the PCI controller to configure itself with the new diff --git a/target/linux/brcm47xx/patches-3.3/234-bcma-always-map-4-bytes.patch b/target/linux/brcm47xx/patches-3.3/234-bcma-always-map-4-bytes.patch new file mode 100644 index 0000000000..adaadcc031 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/234-bcma-always-map-4-bytes.patch @@ -0,0 +1,29 @@ +--- a/drivers/bcma/driver_pci_host.c ++++ b/drivers/bcma/driver_pci_host.c +@@ -119,7 +119,7 @@ static int bcma_extpci_read_config(struc + if (unlikely(!addr)) + goto out; + err = -ENOMEM; +- mmio = ioremap_nocache(addr, len); ++ mmio = ioremap_nocache(addr, sizeof(val)); + if (!mmio) + goto out; + +@@ -171,7 +171,7 @@ static int bcma_extpci_write_config(stru + addr = pc->core->addr + BCMA_CORE_PCI_PCICFG0; + addr |= (func << 8); + addr |= (off & 0xfc); +- mmio = ioremap_nocache(addr, len); ++ mmio = ioremap_nocache(addr, sizeof(val)); + if (!mmio) + goto out; + } +@@ -180,7 +180,7 @@ static int bcma_extpci_write_config(stru + if (unlikely(!addr)) + goto out; + err = -ENOMEM; +- mmio = ioremap_nocache(addr, len); ++ mmio = ioremap_nocache(addr, sizeof(val)); + if (!mmio) + goto out; + diff --git a/target/linux/brcm47xx/patches-3.3/238-bcma-host_pci_devexit.patch b/target/linux/brcm47xx/patches-3.3/238-bcma-host_pci_devexit.patch new file mode 100644 index 0000000000..85b47fa7f8 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/238-bcma-host_pci_devexit.patch @@ -0,0 +1,20 @@ +--- a/drivers/bcma/host_pci.c ++++ b/drivers/bcma/host_pci.c +@@ -222,7 +222,7 @@ err_kfree_bus: + return err; + } + +-static void bcma_host_pci_remove(struct pci_dev *dev) ++static void __devexit bcma_host_pci_remove(struct pci_dev *dev) + { + struct bcma_bus *bus = pci_get_drvdata(dev); + +@@ -278,7 +278,7 @@ static struct pci_driver bcma_pci_bridge + .name = "bcma-pci-bridge", + .id_table = bcma_pci_bridge_tbl, + .probe = bcma_host_pci_probe, +- .remove = bcma_host_pci_remove, ++ .remove = __devexit_p(bcma_host_pci_remove), + .driver.pm = BCMA_PM_OPS, + }; + diff --git a/target/linux/brcm47xx/patches-3.3/239-bcma-add-flush-for-resetctl-write.patch b/target/linux/brcm47xx/patches-3.3/239-bcma-add-flush-for-resetctl-write.patch new file mode 100644 index 0000000000..285c356adf --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/239-bcma-add-flush-for-resetctl-write.patch @@ -0,0 +1,10 @@ +--- a/drivers/bcma/core.c ++++ b/drivers/bcma/core.c +@@ -30,6 +30,7 @@ void bcma_core_disable(struct bcma_devic + udelay(10); + + bcma_awrite32(core, BCMA_RESET_CTL, BCMA_RESET_CTL_RESET); ++ bcma_aread32(core, BCMA_RESET_CTL); + udelay(1); + } + EXPORT_SYMBOL_GPL(bcma_core_disable); diff --git a/target/linux/brcm47xx/patches-3.3/240-bcma-pcie-config-access.patch b/target/linux/brcm47xx/patches-3.3/240-bcma-pcie-config-access.patch new file mode 100644 index 0000000000..874c249b0f --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/240-bcma-pcie-config-access.patch @@ -0,0 +1,104 @@ +--- a/drivers/bcma/driver_pci_host.c ++++ b/drivers/bcma/driver_pci_host.c +@@ -99,19 +99,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 { +@@ -127,8 +127,6 @@ static int bcma_extpci_read_config(struc + val = 0xffffffff; + goto unmap; + } +- +- val = readl(mmio); + } + val >>= (8 * (off & 3)); + +@@ -156,7 +154,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; + +@@ -164,16 +162,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); +@@ -192,12 +196,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; +@@ -205,13 +207,14 @@ static int bcma_extpci_write_config(stru + val = *((const u32 *)buf); + break; + } +- if (dev == 0 && !addr) { ++ if (dev == 0) { + /* accesses to config registers with offsets >= 256 + * requires indirect access. + */ +- addr = (func << 12); +- addr |= (off & 0x0FFF); +- bcma_pcie_write_config(pc, addr, val); ++ if (off >= PCI_CONFIG_SPACE_SIZE) ++ bcma_pcie_write_config(pc, addr, val); ++ else ++ pcicore_write32(pc, addr, val); + } else { + writel(val, mmio); + diff --git a/target/linux/brcm47xx/patches-3.3/280-activate_ssb_support_in_usb.patch b/target/linux/brcm47xx/patches-3.3/280-activate_ssb_support_in_usb.patch new file mode 100644 index 0000000000..c535c05db5 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/280-activate_ssb_support_in_usb.patch @@ -0,0 +1,25 @@ +This prevents the options from being delete with make kernel_oldconfig. +--- + drivers/ssb/Kconfig | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/bcma/Kconfig ++++ b/drivers/bcma/Kconfig +@@ -37,6 +37,7 @@ config BCMA_DRIVER_PCI_HOSTMODE + config BCMA_HOST_SOC + bool + depends on BCMA_DRIVER_MIPS ++ select USB_HCD_BCMA if USB_EHCI_HCD || USB_OHCI_HCD + + config BCMA_SFLASH + bool +--- a/drivers/ssb/Kconfig ++++ b/drivers/ssb/Kconfig +@@ -147,6 +147,7 @@ config SSB_SFLASH + config SSB_EMBEDDED + bool + depends on SSB_DRIVER_MIPS ++ select USB_HCD_SSB if USB_EHCI_HCD || USB_OHCI_HCD + default y + + config SSB_DRIVER_EXTIF diff --git a/target/linux/brcm47xx/patches-3.3/300-fork_cacheflush.patch b/target/linux/brcm47xx/patches-3.3/300-fork_cacheflush.patch new file mode 100644 index 0000000000..686fb1b945 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/300-fork_cacheflush.patch @@ -0,0 +1,11 @@ +--- a/arch/mips/include/asm/cacheflush.h ++++ b/arch/mips/include/asm/cacheflush.h +@@ -32,7 +32,7 @@ + extern void (*flush_cache_all)(void); + extern void (*__flush_cache_all)(void); + extern void (*flush_cache_mm)(struct mm_struct *mm); +-#define flush_cache_dup_mm(mm) do { (void) (mm); } while (0) ++#define flush_cache_dup_mm(mm) flush_cache_mm(mm) + extern void (*flush_cache_range)(struct vm_area_struct *vma, + unsigned long start, unsigned long end); + extern void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page, unsigned long pfn); diff --git a/target/linux/brcm47xx/patches-3.3/301-kmod-fuse-dcache-bug-r4k.patch b/target/linux/brcm47xx/patches-3.3/301-kmod-fuse-dcache-bug-r4k.patch new file mode 100644 index 0000000000..27cd2a7449 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/301-kmod-fuse-dcache-bug-r4k.patch @@ -0,0 +1,28 @@ +--- a/arch/mips/mm/c-r4k.c ++++ b/arch/mips/mm/c-r4k.c +@@ -373,7 +373,7 @@ static inline void local_r4k___flush_cac + } + } + +-static void r4k___flush_cache_all(void) ++void r4k___flush_cache_all(void) + { + r4k_on_each_cpu(local_r4k___flush_cache_all, NULL); + } +@@ -537,7 +537,7 @@ static inline void local_r4k_flush_cache + } + } + +-static void r4k_flush_cache_page(struct vm_area_struct *vma, ++void r4k_flush_cache_page(struct vm_area_struct *vma, + unsigned long addr, unsigned long pfn) + { + struct flush_cache_page_args args; +@@ -1509,3 +1509,7 @@ void __cpuinit r4k_cache_init(void) + coherency_setup(); + #endif + } ++ ++/* fuse package DCACHE BUG patch exports */ ++void (*fuse_flush_cache_all)(void) = r4k___flush_cache_all; ++EXPORT_SYMBOL(fuse_flush_cache_all); diff --git a/target/linux/brcm47xx/patches-3.3/302-kmod-fuse-dcache-bug-fuse.patch b/target/linux/brcm47xx/patches-3.3/302-kmod-fuse-dcache-bug-fuse.patch new file mode 100644 index 0000000000..20b7e47c02 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/302-kmod-fuse-dcache-bug-fuse.patch @@ -0,0 +1,46 @@ +--- a/fs/fuse/dev.c ++++ b/fs/fuse/dev.c +@@ -651,11 +651,20 @@ static int fuse_copy_fill(struct fuse_co + return lock_request(cs->fc, cs->req); + } + ++#ifdef DCACHE_BUG ++extern void (*fuse_flush_cache_all)(void); ++#endif ++ + /* Do as much copy to/from userspace buffer as we can */ + static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size) + { + unsigned ncpy = min(*size, cs->len); + if (val) { ++#ifdef DCACHE_BUG ++ // patch from mailing list, it is very important, otherwise, ++ // can't mount, or ls mount point will hang ++ fuse_flush_cache_all(); ++#endif + if (cs->write) + memcpy(cs->buf, *val, ncpy); + else +--- a/fs/fuse/fuse_i.h ++++ b/fs/fuse/fuse_i.h +@@ -8,6 +8,7 @@ + + #ifndef _FS_FUSE_I_H + #define _FS_FUSE_I_H ++#define DCACHE_BUG + + #include + #include +--- a/fs/fuse/inode.c ++++ b/fs/fuse/inode.c +@@ -1210,6 +1210,10 @@ static int __init fuse_init(void) + printk(KERN_INFO "fuse init (API version %i.%i)\n", + FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); + ++#ifdef DCACHE_BUG ++ printk("fuse init DCACHE_BUG workaround enabled\n"); ++#endif ++ + INIT_LIST_HEAD(&fuse_conn_list); + res = fuse_fs_init(); + if (res) diff --git a/target/linux/brcm47xx/patches-3.3/310-no_highpage.patch b/target/linux/brcm47xx/patches-3.3/310-no_highpage.patch new file mode 100644 index 0000000000..aa55a086f7 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/310-no_highpage.patch @@ -0,0 +1,66 @@ +--- a/arch/mips/include/asm/page.h ++++ b/arch/mips/include/asm/page.h +@@ -48,6 +48,7 @@ + #ifndef __ASSEMBLY__ + + #include ++#include + #include + + extern void build_clear_page(void); +@@ -83,13 +84,16 @@ static inline void clear_user_page(void + flush_data_cache_page((unsigned long)addr); + } + +-extern void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, +- struct page *to); +-struct vm_area_struct; +-extern void copy_user_highpage(struct page *to, struct page *from, +- unsigned long vaddr, struct vm_area_struct *vma); ++static inline void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, ++ struct page *to) ++{ ++ extern void (*flush_data_cache_page)(unsigned long addr); + +-#define __HAVE_ARCH_COPY_USER_HIGHPAGE ++ copy_page(vto, vfrom); ++ if (!cpu_has_ic_fills_f_dc || ++ pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK)) ++ flush_data_cache_page((unsigned long)vto); ++} + + /* + * These are used to make use of C type-checking.. +--- a/arch/mips/mm/init.c ++++ b/arch/mips/mm/init.c +@@ -202,30 +202,6 @@ void kunmap_coherent(void) + preempt_check_resched(); + } + +-void copy_user_highpage(struct page *to, struct page *from, +- unsigned long vaddr, struct vm_area_struct *vma) +-{ +- void *vfrom, *vto; +- +- vto = kmap_atomic(to, KM_USER1); +- if (cpu_has_dc_aliases && cpu_use_kmap_coherent && +- page_mapped(from) && !Page_dcache_dirty(from)) { +- vfrom = kmap_coherent(from, vaddr); +- copy_page(vto, vfrom); +- kunmap_coherent(); +- } else { +- vfrom = kmap_atomic(from, KM_USER0); +- copy_page(vto, vfrom); +- kunmap_atomic(vfrom, KM_USER0); +- } +- if ((!cpu_has_ic_fills_f_dc) || +- pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK)) +- flush_data_cache_page((unsigned long)vto); +- kunmap_atomic(vto, KM_USER1); +- /* Make sure this page is cleared on other CPU's too before using it */ +- smp_wmb(); +-} +- + void copy_to_user_page(struct vm_area_struct *vma, + struct page *page, unsigned long vaddr, void *dst, const void *src, + unsigned long len) diff --git a/target/linux/brcm47xx/patches-3.3/400-arch-bcm47xx.patch b/target/linux/brcm47xx/patches-3.3/400-arch-bcm47xx.patch new file mode 100644 index 0000000000..0933fb6959 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/400-arch-bcm47xx.patch @@ -0,0 +1,56 @@ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -269,3 +269,30 @@ int nvram_getenv(char *name, char *val, + return NVRAM_ERR_ENVNOTFOUND; + } + EXPORT_SYMBOL(nvram_getenv); ++ ++char *nvram_get(const char *name) ++{ ++ char *var, *value, *end, *eq; ++ ++ if (!name) ++ return NULL; ++ ++ if (!nvram_buf[0]) ++ early_nvram_init(); ++ ++ /* Look for name=value and return value */ ++ var = &nvram_buf[sizeof(struct nvram_header)]; ++ end = nvram_buf + sizeof(nvram_buf) - 2; ++ end[0] = end[1] = '\0'; ++ for (; *var; var = value + strlen(value) + 1) { ++ eq = strchr(var, '='); ++ if (!eq) ++ break; ++ value = eq + 1; ++ if ((eq - var) == strlen(name) && strncmp(var, name, (eq - var)) == 0) ++ return value; ++ } ++ ++ return NULL; ++} ++EXPORT_SYMBOL(nvram_get); +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -384,3 +384,20 @@ static int __init bcm47xx_register_flash + return -1; + } + fs_initcall(bcm47xx_register_flash); ++ ++static int __init bcm47xx_register_gpiodev(void) ++{ ++ static struct resource res = { ++ .start = 0xFFFFFFFF, ++ }; ++ struct platform_device *pdev; ++ ++ pdev = platform_device_register_simple("GPIODEV", 0, &res, 1); ++ if (!pdev) { ++ printk(KERN_ERR "bcm47xx: GPIODEV init failed\n"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++device_initcall(bcm47xx_register_gpiodev); diff --git a/target/linux/brcm47xx/patches-3.3/610-pci_ide_fix.patch b/target/linux/brcm47xx/patches-3.3/610-pci_ide_fix.patch new file mode 100644 index 0000000000..f254b203bd --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/610-pci_ide_fix.patch @@ -0,0 +1,14 @@ +--- a/include/linux/ide.h ++++ b/include/linux/ide.h +@@ -195,7 +195,11 @@ static inline void ide_std_init_ports(st + hw->io_ports.ctl_addr = ctl_addr; + } + ++#if defined CONFIG_BCM47XX ++# define MAX_HWIFS 2 ++#else + #define MAX_HWIFS 10 ++#endif + + /* + * Now for the data we need to maintain per-drive: ide_drive_t diff --git a/target/linux/brcm47xx/patches-3.3/700-ssb-gigabit-ethernet-driver.patch b/target/linux/brcm47xx/patches-3.3/700-ssb-gigabit-ethernet-driver.patch new file mode 100644 index 0000000000..6412c8e599 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/700-ssb-gigabit-ethernet-driver.patch @@ -0,0 +1,325 @@ +--- a/drivers/net/ethernet/broadcom/tg3.c ++++ b/drivers/net/ethernet/broadcom/tg3.c +@@ -44,6 +44,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -530,7 +531,8 @@ static void _tw32_flush(struct tg3 *tp, + static inline void tw32_mailbox_flush(struct tg3 *tp, u32 off, u32 val) + { + tp->write32_mbox(tp, off, val); +- if (!tg3_flag(tp, MBOX_WRITE_REORDER) && !tg3_flag(tp, ICH_WORKAROUND)) ++ if (tg3_flag(tp, FLUSH_POSTED_WRITES) || ++ (!tg3_flag(tp, MBOX_WRITE_REORDER) && !tg3_flag(tp, ICH_WORKAROUND))) + tp->read32_mbox(tp, off); + } + +@@ -540,7 +542,7 @@ 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); + } + +@@ -943,7 +945,7 @@ 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; +@@ -957,7 +959,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); +@@ -992,7 +994,12 @@ 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; +@@ -1008,7 +1015,7 @@ static int tg3_writephy(struct tg3 *tp, + udelay(80); + } + +- frame_val = ((tp->phy_addr << MI_COM_PHY_ADDR_SHIFT) & ++ frame_val = ((phy_addr << MI_COM_PHY_ADDR_SHIFT) & + MI_COM_PHY_ADDR_MASK); + frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) & + MI_COM_REG_ADDR_MASK); +@@ -1041,6 +1048,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; +@@ -2942,6 +2954,9 @@ static int tg3_nvram_read(struct tg3 *tp + { + int ret; + ++ if (tg3_flag(tp, IS_SSB_CORE)) ++ return -ENODEV; ++ + if (!tg3_flag(tp, NVRAM)) + return tg3_nvram_read_using_eeprom(tp, offset, val); + +@@ -3024,9 +3039,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; + } + +@@ -3145,6 +3163,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)) +@@ -3491,8 +3514,10 @@ 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 ((tp->phy_id & TG3_PHY_ID_MASK) != TG3_PHY_ID_BCM5750_2 && ++ /* !!! FIXME !!! */ ++ ((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); +@@ -4006,6 +4031,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)) +@@ -7723,6 +7756,11 @@ static int tg3_chip_reset(struct tg3 *tp + } + } + ++ 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) { + tw32(VCPU_STATUS, tr32(VCPU_STATUS) | VCPU_STATUS_DRV_RESET); + tw32(GRC_VCPU_EXT_CTRL, +@@ -7819,6 +7857,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) { +@@ -9233,6 +9279,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 +@@ -9522,6 +9573,11 @@ static int tg3_request_firmware(struct t + return -ENOENT; + } + ++ 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 +@@ -10945,6 +11001,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; + +@@ -11906,7 +11967,7 @@ static int tg3_ioctl(struct net_device * + 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; +@@ -11922,7 +11983,7 @@ static int tg3_ioctl(struct net_device * + 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; +@@ -12656,6 +12717,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 << +@@ -12922,6 +12990,9 @@ static int tg3_nvram_write_block(struct + { + int ret; + ++ if (tg3_flag(tp, IS_SSB_CORE)) ++ return -ENODEV; ++ + if (tg3_flag(tp, EEPROM_WRITE_PROT)) { + tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl & + ~GRC_LCLCTRL_GPIO_OUTPUT1); +@@ -14368,6 +14439,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 +@@ -14784,6 +14860,8 @@ 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]); + #ifdef CONFIG_SPARC + if (!tg3_get_default_macaddr_sparc(tp)) + return 0; +@@ -15282,6 +15360,7 @@ static char * __devinit tg3_phy_string(s + case TG3_PHY_ID_BCM5704: return "5704"; + case TG3_PHY_ID_BCM5705: return "5705"; + case TG3_PHY_ID_BCM5750: return "5750"; ++ case TG3_PHY_ID_BCM5750_2: return "5750-2"; + case TG3_PHY_ID_BCM5752: return "5752"; + case TG3_PHY_ID_BCM5714: return "5714"; + case TG3_PHY_ID_BCM5780: return "5780"; +@@ -15492,6 +15571,13 @@ 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_have_roboswitch(pdev)) ++ tg3_flag_set(tp, ROBOSWITCH); ++ } + + /* The word/byte swap controls here control register access byte + * swapping. DMA data byte swapping is controlled in the GRC_MODE +--- a/drivers/net/ethernet/broadcom/tg3.h ++++ b/drivers/net/ethernet/broadcom/tg3.h +@@ -2940,6 +2940,9 @@ 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, + + /* Add new flags before this comment and TG3_FLAG_NUMBER_OF_FLAGS */ + TG3_FLAG_NUMBER_OF_FLAGS, /* Last entry in enum TG3_FLAGS */ +@@ -3089,6 +3092,7 @@ struct tg3 { + #define TG3_PHY_ID_BCM5704 0x60008190 + #define TG3_PHY_ID_BCM5705 0x600081a0 + #define TG3_PHY_ID_BCM5750 0x60008180 ++#define TG3_PHY_ID_BCM5750_2 0xbc050cd0 + #define TG3_PHY_ID_BCM5752 0x60008100 + #define TG3_PHY_ID_BCM5714 0x60008340 + #define TG3_PHY_ID_BCM5780 0x60008350 +@@ -3126,7 +3130,7 @@ struct tg3 { + (X) == TG3_PHY_ID_BCM5906 || (X) == TG3_PHY_ID_BCM5761 || \ + (X) == TG3_PHY_ID_BCM5718C || (X) == TG3_PHY_ID_BCM5718S || \ + (X) == TG3_PHY_ID_BCM57765 || (X) == TG3_PHY_ID_BCM5719C || \ +- (X) == TG3_PHY_ID_BCM8002) ++ (X) == TG3_PHY_ID_BCM8002 || (X) == TG3_PHY_ID_BCM5750_2) + + u32 phy_flags; + #define TG3_PHYFLG_IS_LOW_POWER 0x00000001 diff --git a/target/linux/brcm47xx/patches-3.3/812-disable_wgt634u_crap.patch b/target/linux/brcm47xx/patches-3.3/812-disable_wgt634u_crap.patch new file mode 100644 index 0000000000..a71e6bdf32 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/812-disable_wgt634u_crap.patch @@ -0,0 +1,180 @@ +--- 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 bus.o sprom.o +-obj-$(CONFIG_BCM47XX_SSB) += wgt634u.o +--- a/arch/mips/bcm47xx/wgt634u.c ++++ /dev/null +@@ -1,170 +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 +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/* 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; +- +- 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"); +- +- 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.3/820-wgt634u-nvram-fix.patch b/target/linux/brcm47xx/patches-3.3/820-wgt634u-nvram-fix.patch new file mode 100644 index 0000000000..baf339dca5 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/820-wgt634u-nvram-fix.patch @@ -0,0 +1,305 @@ +The Netgear wgt634u uses a different format for storing the +configuration. This patch is needed to read out the correct +configuration. The cfe_env.c file uses a different method way to read +out the configuration than the in kernel cfe config reader. + +--- a/arch/mips/bcm47xx/Makefile ++++ b/arch/mips/bcm47xx/Makefile +@@ -3,4 +3,4 @@ + # under Linux. + # + +-obj-y += gpio.o irq.o nvram.o prom.o serial.o setup.o time.o bus.o sprom.o ++obj-y += gpio.o irq.o nvram.o prom.o serial.o setup.o time.o bus.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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#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: ++ * ++ * ++ * ++ * 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: ++ * ++ * = ++ * ++ * If bit 0 (low bit) is set, the length is an 8-bit value. ++ * If bit 0 (low bit) is clear, the length is a 16-bit value ++ * ++ * Bit 7 set indicates "user" TLVs. In this case, bit 0 still ++ * indicates the size of the length field. ++ * ++ * Flags are from the constants below: ++ * ++ */ ++#define ENV_LENGTH_16BITS 0x00 /* for low bit */ ++#define ENV_LENGTH_8BITS 0x01 ++ ++#define ENV_TYPE_USER 0x80 ++ ++#define ENV_CODE_SYS(n,l) (((n)<<1)|(l)) ++#define ENV_CODE_USER(n,l) ((((n)<<1)|(l)) | ENV_TYPE_USER) ++ ++/* ++ * The actual TLV types we support ++ */ ++ ++#define ENV_TLV_TYPE_END 0x00 ++#define ENV_TLV_TYPE_ENV ENV_CODE_SYS(0,ENV_LENGTH_8BITS) ++ ++/* ++ * Environment variable flags ++ */ ++ ++#define ENV_FLG_NORMAL 0x00 /* normal read/write */ ++#define ENV_FLG_BUILTIN 0x01 /* builtin - not stored in flash */ ++#define ENV_FLG_READONLY 0x02 /* read-only - cannot be changed */ ++ ++#define ENV_FLG_MASK 0xFF /* mask of attributes we keep */ ++#define ENV_FLG_ADMIN 0x100 /* lets us internally override permissions */ ++ ++ ++/* ********************************************************************* ++ * _nvram_read(buffer,offset,length) ++ * ++ * Read data from the NVRAM device ++ * ++ * Input parameters: ++ * buffer - destination buffer ++ * offset - offset of data to read ++ * length - number of bytes to read ++ * ++ * Return value: ++ * number of bytes read, or <0 if error occured ++ ********************************************************************* */ ++static int ++_nvram_read(unsigned char *nv_buf, unsigned char *buffer, int offset, int length) ++{ ++ int i; ++ if (offset > NVRAM_SIZE) ++ return -1; ++ ++ for ( i = 0; i < length; i++) { ++ buffer[i] = ((volatile unsigned char*)nv_buf)[offset + i]; ++ } ++ return length; ++} ++ ++ ++static char* ++_strnchr(const char *dest,int c,size_t cnt) ++{ ++ while (*dest && (cnt > 0)) { ++ if (*dest == c) return (char *) dest; ++ dest++; ++ cnt--; ++ } ++ return NULL; ++} ++ ++ ++ ++/* ++ * Core support API: Externally visible. ++ */ ++ ++/* ++ * Get the value of an NVRAM variable ++ * @param name name of variable to get ++ * @return value of variable or NULL if undefined ++ */ ++ ++char* ++cfe_env_get(unsigned char *nv_buf, char* name) ++{ ++ int size; ++ unsigned char *buffer; ++ unsigned char *ptr; ++ unsigned char *envval; ++ unsigned int reclen; ++ unsigned int rectype; ++ int offset; ++ int flg; ++ ++ if (!strcmp(name, "nvram_type")) ++ return "cfe"; ++ ++ size = NVRAM_SIZE; ++ buffer = &_nvdata[0]; ++ ++ ptr = buffer; ++ offset = 0; ++ ++ /* Read the record type and length */ ++ if (_nvram_read(nv_buf, ptr,offset,1) != 1) { ++ goto error; ++ } ++ ++ while ((*ptr != ENV_TLV_TYPE_END) && (size > 1)) { ++ ++ /* Adjust pointer for TLV type */ ++ rectype = *(ptr); ++ offset++; ++ size--; ++ ++ /* ++ * Read the length. It can be either 1 or 2 bytes ++ * depending on the code ++ */ ++ if (rectype & ENV_LENGTH_8BITS) { ++ /* Read the record type and length - 8 bits */ ++ if (_nvram_read(nv_buf, ptr,offset,1) != 1) { ++ goto error; ++ } ++ reclen = *(ptr); ++ size--; ++ offset++; ++ } ++ else { ++ /* Read the record type and length - 16 bits, MSB first */ ++ if (_nvram_read(nv_buf, ptr,offset,2) != 2) { ++ goto error; ++ } ++ reclen = (((unsigned int) *(ptr)) << 8) + (unsigned int) *(ptr+1); ++ size -= 2; ++ offset += 2; ++ } ++ ++ if (reclen > size) ++ break; /* should not happen, bad NVRAM */ ++ ++ switch (rectype) { ++ case ENV_TLV_TYPE_ENV: ++ /* Read the TLV data */ ++ if (_nvram_read(nv_buf, ptr,offset,reclen) != reclen) ++ goto error; ++ flg = *ptr++; ++ envval = (unsigned char *) _strnchr(ptr,'=',(reclen-1)); ++ if (envval) { ++ *envval++ = '\0'; ++ memcpy(_valuestr,envval,(reclen-1)-(envval-ptr)); ++ _valuestr[(reclen-1)-(envval-ptr)] = '\0'; ++#if 0 ++ printk(KERN_INFO "NVRAM:%s=%s\n", ptr, _valuestr); ++#endif ++ if(!strcmp(ptr, name)){ ++ return _valuestr; ++ } ++ if((strlen(ptr) > 1) && !strcmp(&ptr[1], name)) ++ return _valuestr; ++ } ++ break; ++ ++ default: ++ /* Unknown TLV type, skip it. */ ++ break; ++ } ++ ++ /* ++ * Advance to next TLV ++ */ ++ ++ size -= (int)reclen; ++ offset += reclen; ++ ++ /* Read the next record type */ ++ ptr = buffer; ++ if (_nvram_read(nv_buf, ptr,offset,1) != 1) ++ goto error; ++ } ++ ++error: ++ return NULL; ++ ++} ++ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -25,6 +25,8 @@ + #include + + static char nvram_buf[NVRAM_SPACE]; ++static int cfe_env; ++extern char *cfe_env_get(char *nv_buf, const char *name); + + /* Probe for NVRAM header */ + static void early_nvram_init_pflash(void) +@@ -58,6 +60,25 @@ static void early_nvram_init_pflash(void + break; + #endif + } ++ cfe_env = 0; ++ ++ /* XXX: hack for supporting the CFE environment stuff on WGT634U */ ++ if (lim >= 8 * 1024 * 1024) { ++ src = (u32 *) KSEG1ADDR(base + 8 * 1024 * 1024 - 0x2000); ++ dst = (u32 *) nvram_buf; ++ ++ if ((*src & 0xff00ff) == 0x000001) { ++ printk("early_nvram_init: WGT634U NVRAM found.\n"); ++ ++ for (i = 0; i < 0x1ff0; i++) { ++ if (*src == 0xFFFFFFFF) ++ break; ++ *dst++ = *src++; ++ } ++ cfe_env = 1; ++ return; ++ } ++ } + + off = FLASH_MIN; + while (off <= lim) { +@@ -252,6 +273,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; +@@ -280,6 +307,9 @@ char *nvram_get(const char *name) + if (!nvram_buf[0]) + early_nvram_init(); + ++ if (cfe_env) ++ return cfe_env_get(nvram_buf, name); ++ + /* Look for name=value and return value */ + var = &nvram_buf[sizeof(struct nvram_header)]; + end = nvram_buf + sizeof(nvram_buf) - 2; diff --git a/target/linux/brcm47xx/patches-3.3/830-tg3_add_pci_ids.patch b/target/linux/brcm47xx/patches-3.3/830-tg3_add_pci_ids.patch new file mode 100644 index 0000000000..a10fc9f19c --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/830-tg3_add_pci_ids.patch @@ -0,0 +1,47 @@ +commit 812bcf8f47b45a206948008144233bc47d5747ac +Author: Hauke Mehrtens +Date: Thu Dec 16 20:01:17 2010 +0100 + + Revert "tg3: Remove 5720, 5750, and 5750M" + + This reverts commit 67b284d476bcb3d100e946da23d6cf9acfd0465c. + +--- a/drivers/net/ethernet/broadcom/tg3.c ++++ b/drivers/net/ethernet/broadcom/tg3.c +@@ -248,9 +248,12 @@ static DEFINE_PCI_DEVICE_TABLE(tg3_pci_t + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5901_2)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5704S_2)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705F)}, ++ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5720)}, + {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_5750M)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751M)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751F)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5752)}, +--- a/include/linux/pci_ids.h ++++ b/include/linux/pci_ids.h +@@ -2105,6 +2105,7 @@ + #define PCI_DEVICE_ID_NX2_57711E 0x1650 + #define PCI_DEVICE_ID_TIGON3_5705 0x1653 + #define PCI_DEVICE_ID_TIGON3_5705_2 0x1654 ++#define PCI_DEVICE_ID_TIGON3_5720 0x1658 + #define PCI_DEVICE_ID_TIGON3_5721 0x1659 + #define PCI_DEVICE_ID_TIGON3_5722 0x165a + #define PCI_DEVICE_ID_TIGON3_5723 0x165b +@@ -2120,11 +2121,13 @@ + #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 + #define PCI_DEVICE_ID_TIGON3_5754 0x167a + #define PCI_DEVICE_ID_TIGON3_5755 0x167b ++#define PCI_DEVICE_ID_TIGON3_5750M 0x167c + #define PCI_DEVICE_ID_TIGON3_5751M 0x167d + #define PCI_DEVICE_ID_TIGON3_5751F 0x167e + #define PCI_DEVICE_ID_TIGON3_5787F 0x167f diff --git a/target/linux/brcm47xx/patches-3.3/900-bcm47xx_wdt-noprescale.patch b/target/linux/brcm47xx/patches-3.3/900-bcm47xx_wdt-noprescale.patch new file mode 100644 index 0000000000..d60247e91a --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/900-bcm47xx_wdt-noprescale.patch @@ -0,0 +1,104 @@ +--- a/drivers/watchdog/bcm47xx_wdt.c ++++ b/drivers/watchdog/bcm47xx_wdt.c +@@ -31,6 +31,7 @@ + + #define WDT_DEFAULT_TIME 30 /* seconds */ + #define WDT_MAX_TIME 255 /* seconds */ ++#define WDT_SHIFT 15 /* 32.768 KHz on cores with slow WDT clock */ + + static int wdt_time = WDT_DEFAULT_TIME; + static int nowayout = WATCHDOG_NOWAYOUT; +@@ -50,20 +51,20 @@ static unsigned long bcm47xx_wdt_busy; + static char expect_release; + static struct timer_list wdt_timer; + static atomic_t ticks; ++static int needs_sw_scale; + +-static inline void bcm47xx_wdt_hw_start(void) ++static inline void bcm47xx_wdt_hw_start(u32 ticks) + { +- /* this is 2,5s on 100Mhz clock and 2s on 133 Mhz */ + switch (bcm47xx_bus_type) { + #ifdef CONFIG_BCM47XX_SSB + case BCM47XX_BUS_TYPE_SSB: +- ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 0xfffffff); ++ ssb_watchdog_timer_set(&bcm47xx_bus.ssb, ticks); + break; + #endif + #ifdef CONFIG_BCM47XX_BCMA + case BCM47XX_BUS_TYPE_BCMA: + bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc, +- 0xfffffff); ++ ticks); + break; + #endif + } +@@ -88,33 +89,34 @@ static inline int bcm47xx_wdt_hw_stop(vo + static void bcm47xx_timer_tick(unsigned long unused) + { + if (!atomic_dec_and_test(&ticks)) { +- bcm47xx_wdt_hw_start(); ++ /* This is 2,5s on 100Mhz clock and 2s on 133 Mhz */ ++ bcm47xx_wdt_hw_start(0xfffffff); + mod_timer(&wdt_timer, jiffies + HZ); + } else { +- printk(KERN_CRIT DRV_NAME "Watchdog will fire soon!!!\n"); ++ printk(KERN_CRIT DRV_NAME ": Watchdog will fire soon!!!\n"); + } + } + +-static inline void bcm47xx_wdt_pet(void) ++static void bcm47xx_wdt_pet(void) + { +- atomic_set(&ticks, wdt_time); ++ if(needs_sw_scale) ++ atomic_set(&ticks, wdt_time); ++ else ++ bcm47xx_wdt_hw_start(wdt_time << WDT_SHIFT); + } + + static void bcm47xx_wdt_start(void) + { + bcm47xx_wdt_pet(); +- bcm47xx_timer_tick(0); +-} +- +-static void bcm47xx_wdt_pause(void) +-{ +- del_timer_sync(&wdt_timer); +- bcm47xx_wdt_hw_stop(); ++ if(needs_sw_scale) ++ bcm47xx_timer_tick(0); + } + + static void bcm47xx_wdt_stop(void) + { +- bcm47xx_wdt_pause(); ++ if(needs_sw_scale) ++ del_timer_sync(&wdt_timer); ++ bcm47xx_wdt_hw_stop(); + } + + static int bcm47xx_wdt_settimeout(int new_time) +@@ -266,7 +268,20 @@ static int __init bcm47xx_wdt_init(void) + if (bcm47xx_wdt_hw_stop() < 0) + return -ENODEV; + +- setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L); ++ /* FIXME Other cores */ ++#ifdef BCM47XX_BUS_TYPE_BCMA ++ if(bcm47xx_bus_type == BCM47XX_BUS_TYPE_BCMA && ++ bcm47xx_bus.ssb.chip_id == 0x5354) { ++ /* Slow WDT clock, no pre-scaling */ ++ needs_sw_scale = 0; ++ } else { ++#endif ++ /* Fast WDT clock, needs software pre-scaling */ ++ needs_sw_scale = 1; ++ setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L); ++#ifdef BCM47XX_BUS_TYPE_BCMA ++ } ++#endif + + if (bcm47xx_wdt_settimeout(wdt_time)) { + bcm47xx_wdt_settimeout(WDT_DEFAULT_TIME); diff --git a/target/linux/brcm47xx/patches-3.3/920-cache-wround.patch b/target/linux/brcm47xx/patches-3.3/920-cache-wround.patch new file mode 100644 index 0000000000..0f6f76596b --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/920-cache-wround.patch @@ -0,0 +1,138 @@ +--- a/arch/mips/include/asm/r4kcache.h ++++ b/arch/mips/include/asm/r4kcache.h +@@ -20,10 +20,28 @@ + #ifdef CONFIG_BCM47XX + #include + #include +-#define BCM4710_DUMMY_RREG() ((void) *((u8 *) KSEG1ADDR(SSB_ENUM_BASE))) ++#define BCM4710_DUMMY_RREG() bcm4710_dummy_rreg() ++ ++static inline unsigned long bcm4710_dummy_rreg(void) ++{ ++ return *(volatile unsigned long *)(KSEG1ADDR(SSB_ENUM_BASE)); ++} ++ ++#define BCM4710_FILL_TLB(addr) bcm4710_fill_tlb((void *)(addr)) ++ ++static inline unsigned long bcm4710_fill_tlb(void *addr) ++{ ++ return *(unsigned long *)addr; ++} ++ ++#define BCM4710_PROTECTED_FILL_TLB(addr) bcm4710_protected_fill_tlb((void *)(addr)) ++ ++static inline void bcm4710_protected_fill_tlb(void *addr) ++{ ++ unsigned long x; ++ get_dbe(x, (unsigned long *)addr);; ++} + +-#define BCM4710_FILL_TLB(addr) (*(volatile unsigned long *)(addr)) +-#define BCM4710_PROTECTED_FILL_TLB(addr) ({ unsigned long x; get_dbe(x, (volatile unsigned long *)(addr)); }) + #else + #define BCM4710_DUMMY_RREG() + +--- a/arch/mips/mm/tlbex.c ++++ b/arch/mips/mm/tlbex.c +@@ -914,6 +914,9 @@ build_get_pgde32(u32 **p, unsigned int t + #endif + uasm_i_addu(p, ptr, tmp, ptr); + #else ++#ifdef CONFIG_BCM47XX ++ uasm_i_nop(p); ++#endif + UASM_i_LA_mostly(p, ptr, pgdc); + #endif + uasm_i_mfc0(p, tmp, C0_BADVADDR); /* get faulting address */ +@@ -1264,12 +1267,12 @@ static void __cpuinit build_r4000_tlb_re + /* No need for uasm_i_nop */ + } + +-#ifdef CONFIG_BCM47XX +- uasm_i_nop(&p); +-#endif + #ifdef CONFIG_64BIT + build_get_pmde64(&p, &l, &r, K0, K1); /* get pmd in K1 */ + #else ++# ifdef CONFIG_BCM47XX ++ uasm_i_nop(&p); ++# endif + build_get_pgde32(&p, K0, K1); /* get pgd in K1 */ + #endif + +@@ -1281,6 +1284,9 @@ static void __cpuinit build_r4000_tlb_re + build_update_entries(&p, K0, K1); + build_tlb_write_entry(&p, &l, &r, tlb_random); + uasm_l_leave(&l, p); ++#ifdef CONFIG_BCM47XX ++ uasm_i_nop(&p); ++#endif + uasm_i_eret(&p); /* return from trap */ + } + #ifdef CONFIG_HUGETLB_PAGE +@@ -1797,12 +1803,12 @@ build_r4000_tlbchange_handler_head(u32 * + { + struct work_registers wr = build_get_work_registers(p); + +-#ifdef CONFIG_BCM47XX +- uasm_i_nop(p); +-#endif + #ifdef CONFIG_64BIT + build_get_pmde64(p, l, r, wr.r1, wr.r2); /* get pmd in ptr */ + #else ++# ifdef CONFIG_BCM47XX ++ uasm_i_nop(p); ++# endif + build_get_pgde32(p, wr.r1, wr.r2); /* get pgd in ptr */ + #endif + +@@ -1841,6 +1847,9 @@ build_r4000_tlbchange_handler_tail(u32 * + build_tlb_write_entry(p, l, r, tlb_indexed); + uasm_l_leave(l, *p); + build_restore_work_registers(p); ++#ifdef CONFIG_BCM47XX ++ uasm_i_nop(p); ++#endif + uasm_i_eret(p); /* return from trap */ + + #ifdef CONFIG_64BIT +--- a/arch/mips/kernel/genex.S ++++ b/arch/mips/kernel/genex.S +@@ -22,6 +22,19 @@ + #include + #include + ++#ifdef CONFIG_BCM47XX ++# ifdef eret ++# undef eret ++# endif ++# define eret \ ++ .set push; \ ++ .set noreorder; \ ++ nop; \ ++ nop; \ ++ eret; \ ++ .set pop; ++#endif ++ + #define PANIC_PIC(msg) \ + .set push; \ + .set reorder; \ +@@ -54,7 +67,6 @@ NESTED(except_vec3_generic, 0, sp) + .set noat + #ifdef CONFIG_BCM47XX + nop +- nop + #endif + #if R5432_CP0_INTERRUPT_WAR + mfc0 k0, CP0_INDEX +@@ -79,6 +91,9 @@ NESTED(except_vec3_r4000, 0, sp) + .set push + .set mips3 + .set noat ++#ifdef CONFIG_BCM47XX ++ nop ++#endif + mfc0 k1, CP0_CAUSE + li k0, 31<<2 + andi k1, k1, 0x7c diff --git a/target/linux/brcm47xx/patches-3.3/940-bcm47xx-yenta.patch b/target/linux/brcm47xx/patches-3.3/940-bcm47xx-yenta.patch new file mode 100644 index 0000000000..c65958eda8 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/940-bcm47xx-yenta.patch @@ -0,0 +1,46 @@ +--- a/drivers/pcmcia/yenta_socket.c ++++ b/drivers/pcmcia/yenta_socket.c +@@ -920,6 +920,8 @@ static unsigned int yenta_probe_irq(stru + * Probe for usable interrupts using the force + * register to generate bogus card status events. + */ ++#ifndef CONFIG_BCM47XX ++ /* WRT54G3G does not like this */ + cb_writel(socket, CB_SOCKET_EVENT, -1); + cb_writel(socket, CB_SOCKET_MASK, CB_CSTSMASK); + reg = exca_readb(socket, I365_CSCINT); +@@ -935,6 +937,7 @@ static unsigned int yenta_probe_irq(stru + } + cb_writel(socket, CB_SOCKET_MASK, 0); + exca_writeb(socket, I365_CSCINT, reg); ++#endif + + mask = probe_irq_mask(val) & 0xffff; + +@@ -1019,6 +1022,10 @@ static void yenta_get_socket_capabilitie + else + socket->socket.irq_mask = 0; + ++ /* irq mask probing is broken for the WRT54G3G */ ++ if (socket->socket.irq_mask == 0) ++ socket->socket.irq_mask = 0x6f8; ++ + dev_printk(KERN_INFO, &socket->dev->dev, + "ISA IRQ mask 0x%04x, PCI irq %d\n", + socket->socket.irq_mask, socket->cb_irq); +@@ -1257,6 +1264,15 @@ static int __devinit yenta_probe(struct + dev_printk(KERN_INFO, &dev->dev, + "Socket status: %08x\n", cb_readl(socket, CB_SOCKET_STATE)); + ++ /* Generate an interrupt on card insert/remove */ ++ config_writew(socket, CB_SOCKET_MASK, CB_CSTSMASK | CB_CDMASK); ++ ++ /* Set up Multifunction Routing Status Register */ ++ config_writew(socket, 0x8C, 0x1000 /* MFUNC3 to GPIO3 */ | 0x2 /* MFUNC0 to INTA */); ++ ++ /* Switch interrupts to parallelized */ ++ config_writeb(socket, 0x92, 0x64); ++ + yenta_fixup_parent_bridge(dev->subordinate); + + /* Register it with the pcmcia layer.. */ diff --git a/target/linux/brcm47xx/patches-3.3/976-ssb_increase_pci_delay.patch b/target/linux/brcm47xx/patches-3.3/976-ssb_increase_pci_delay.patch new file mode 100644 index 0000000000..eb70c81e65 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/976-ssb_increase_pci_delay.patch @@ -0,0 +1,11 @@ +--- a/drivers/ssb/driver_pcicore.c ++++ b/drivers/ssb/driver_pcicore.c +@@ -376,7 +376,7 @@ static void __devinit ssb_pcicore_init_h + set_io_port_base(ssb_pcicore_controller.io_map_base); + /* Give some time to the PCI controller to configure itself with the new + * values. Not waiting at this point causes crashes of the machine. */ +- mdelay(10); ++ mdelay(300); + register_pci_controller(&ssb_pcicore_controller); + } + diff --git a/target/linux/brcm47xx/patches-3.3/980-wnr834b_no_cardbus_invariant.patch b/target/linux/brcm47xx/patches-3.3/980-wnr834b_no_cardbus_invariant.patch new file mode 100644 index 0000000000..42f00ddc31 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/980-wnr834b_no_cardbus_invariant.patch @@ -0,0 +1,13 @@ +--- a/arch/mips/bcm47xx/setup.c ++++ b/arch/mips/bcm47xx/setup.c +@@ -172,6 +172,10 @@ static int bcm47xx_get_invariants(struct + if (nvram_getenv("cardbus", buf, sizeof(buf)) >= 0) + iv->has_cardbus_slot = !!simple_strtoul(buf, NULL, 10); + ++ /* Do not indicate cardbus for Netgear WNR834B V1 and V2 */ ++ if (iv->boardinfo.type == 0x0472 && iv->has_cardbus_slot) ++ iv->has_cardbus_slot = 0; ++ + return 0; + } + diff --git a/target/linux/brcm47xx/patches-3.3/999-wl_exports.patch b/target/linux/brcm47xx/patches-3.3/999-wl_exports.patch new file mode 100644 index 0000000000..550f4c9683 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.3/999-wl_exports.patch @@ -0,0 +1,22 @@ +--- a/arch/mips/bcm47xx/nvram.c ++++ b/arch/mips/bcm47xx/nvram.c +@@ -24,7 +24,8 @@ + #include + #include + +-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 +@@ -57,6 +57,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 */ +