brcm47xx: add support for kernel 3.2

SVN-Revision: 29756
owl
Hauke Mehrtens 2012-01-15 21:18:34 +00:00
parent c3d134af12
commit 034cf5643f
47 changed files with 7484 additions and 0 deletions

View File

@ -0,0 +1,142 @@
# 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_POPULATES_NODE_MAP=y
# CONFIG_ARCH_SUPPORTS_MSI is not set
CONFIG_ARCH_SUSPEND_POSSIBLE=y
# CONFIG_ARPD is not set
# CONFIG_ATH79 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_POSSIBLE=y
CONFIG_BCMA_SFLASH=y
# CONFIG_BUG is not set
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_CEVT_R4K=y
CONFIG_CEVT_R4K_LIB=y
CONFIG_CFE=y
CONFIG_CMDLINE="root=/dev/mtdblock2 rootfstype=squashfs,jffs2 noinitrd console=ttyS0,115200"
CONFIG_CMDLINE_BOOL=y
# CONFIG_CMDLINE_OVERRIDE is not set
CONFIG_CPU_HAS_PREFETCH=y
CONFIG_CPU_HAS_SYNC=y
CONFIG_CPU_LITTLE_ENDIAN=y
CONFIG_CPU_MIPS32=y
CONFIG_CPU_MIPS32_R1=y
# CONFIG_CPU_MIPS32_R2 is not set
CONFIG_CPU_MIPSR1=y
CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
CONFIG_CPU_SUPPORTS_HIGHMEM=y
CONFIG_CSRC_R4K=y
CONFIG_CSRC_R4K_LIB=y
CONFIG_DECOMPRESS_LZMA=y
CONFIG_DMA_NONCOHERENT=y
CONFIG_GENERIC_ATOMIC64=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
CONFIG_GENERIC_CMOS_UPDATE=y
CONFIG_GENERIC_GPIO=y
CONFIG_GENERIC_IRQ_SHOW=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_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_LANTIQ is not set
CONFIG_LEDS_GPIO=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_NEED_DMA_MAP_STATE=y
CONFIG_NEED_PER_CPU_KM=y
CONFIG_NO_HZ=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

View File

@ -0,0 +1,142 @@
From b7d9f9cd6a8e463c1061ea29ed3e614403625024 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sun, 17 Jul 2011 14:51:47 +0200
Subject: [PATCH 12/26] bcma: move parallel flash into a union
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
arch/mips/bcm47xx/nvram.c | 3 +
drivers/bcma/driver_mips.c | 1 +
include/linux/bcma/bcma_driver_chipcommon.h | 73 ++++++++++++++++++++++++++-
3 files changed, 76 insertions(+), 1 deletions(-)
--- 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
@@ -108,10 +108,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 */
@@ -300,6 +358,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)
*/
@@ -309,6 +373,10 @@ struct bcma_chipcommon_pmu {
};
#ifdef CONFIG_BCMA_DRIVER_MIPS
+enum bcma_flash_type {
+ BCMA_PFLASH,
+};
+
struct bcma_pflash {
u8 buswidth;
u32 window;
@@ -334,7 +402,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];

View File

@ -0,0 +1,681 @@
From a62940e988526c881966a8c72cc28c95fca89f3c Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sun, 17 Jul 2011 14:53:07 +0200
Subject: [PATCH 13/26] bcma: add serial flash support to bcma
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
drivers/bcma/Kconfig | 5 +
drivers/bcma/Makefile | 1 +
drivers/bcma/bcma_private.h | 5 +
drivers/bcma/driver_chipcommon_sflash.c | 555 +++++++++++++++++++++++++++
drivers/bcma/driver_mips.c | 8 +-
include/linux/bcma/bcma_driver_chipcommon.h | 24 ++
6 files changed, 597 insertions(+), 1 deletions(-)
create mode 100644 drivers/bcma/driver_chipcommon_sflash.c
--- 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
@@ -41,6 +41,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,555 @@
+/*
+ * Broadcom SiliconBackplane chipcommon serial flash interface
+ *
+ * Copyright 2011, Jonas Gorski <jonas.gorski@gmail.com>
+ * Copyright 2010, Broadcom Corporation
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_driver_chipcommon.h>
+#include <linux/delay.h>
+
+#include "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;
+
+
+ if (cc->core->id.rev == 12)
+ from = (u8 *)KSEG1ADDR(BCMA_FLASH2 + offset);
+ else
+ 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)
+{
+ struct bcma_bus *bus = cc->core->bus;
+ int ret = 0;
+ bool is4712b0 = (bus->chipinfo.id == 0x4712) && (bus->chipinfo.rev == 3);
+ u32 mask;
+
+
+ /* Enable writes */
+ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_WREN);
+ if (is4712b0) {
+ mask = 1 << 14;
+ bcma_sflash_write_u8(cc, offset, *buf++);
+ /* Set chip select */
+ bcma_cc_set32(cc, BCMA_CC_GPIOOUT, mask);
+ /* Issue a page program with the first byte */
+ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_PP);
+ ret = 1;
+ offset++;
+ len--;
+ while (len > 0) {
+ if ((offset & 255) == 0) {
+ /* Page boundary, drop cs and return */
+ bcma_cc_mask32(cc, BCMA_CC_GPIOOUT, ~mask);
+ udelay(1);
+ if (!bcma_sflash_poll(cc, offset)) {
+ /* Flash rejected command */
+ return -EAGAIN;
+ }
+ return ret;
+ } else {
+ /* Write single byte */
+ bcma_sflash_cmd(cc, *buf++);
+ }
+ ret++;
+ offset++;
+ len--;
+ }
+ /* All done, drop cs */
+ bcma_cc_mask32(cc, BCMA_CC_GPIOOUT, ~mask);
+ udelay(1);
+ if (!bcma_sflash_poll(cc, offset)) {
+ /* Flash rejected command */
+ return -EAGAIN;
+ }
+ } else if (cc->core->id.rev >= 20) {
+ 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);
+ ret = 1;
+ 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 ret;
+ } else {
+ /* Write single byte */
+ bcma_sflash_cmd(cc,
+ BCMA_CC_FLASHCTL_ST_CSA |
+ *buf++);
+ }
+ ret++;
+ 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;
+ }
+ } else {
+ ret = 1;
+ bcma_sflash_write_u8(cc, offset, *buf);
+ /* Page program */
+ bcma_sflash_cmd(cc, BCMA_CC_FLASHCTL_ST_PP);
+ }
+ return ret;
+}
+
+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.
+ */
+ bcma_sflash_cmd(cc, (sfl->blocksize < (64 * 1024)) ? BCMA_CC_FLASHCTL_ST_SSE : 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;
+}
+
+/*
+ * writes the appropriate range of flash, a NULL buf simply erases
+ * the region of flash
+ */
+int bcma_sflash_commit(struct bcma_drv_cc *cc, u32 offset, u32 len,
+ const u8 *buf)
+{
+ struct bcma_sflash *sfl;
+ u8 *block = NULL, *cur_ptr, *blk_ptr;
+ u32 blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder;
+ u32 blk_offset, blk_len, copied;
+ int bytes, ret = 0;
+
+ /* Check address range */
+ if (len <= 0)
+ return 0;
+
+ sfl = &cc->sflash;
+ if ((offset + len) > sfl->size)
+ return -EINVAL;
+
+ blocksize = sfl->blocksize;
+ mask = blocksize - 1;
+
+ /* Allocate a block of mem */
+ block = kmalloc(blocksize, GFP_KERNEL);
+ if (!block)
+ return -ENOMEM;
+
+ while (len) {
+ /* Align offset */
+ cur_offset = offset & ~mask;
+ cur_length = blocksize;
+ cur_ptr = block;
+
+ remainder = blocksize - (offset & mask);
+ if (len < remainder)
+ cur_retlen = len;
+ else
+ cur_retlen = remainder;
+
+ /* buf == NULL means erase only */
+ if (buf) {
+ /* Copy existing data into holding block if necessary */
+ if ((offset & mask) || (len < blocksize)) {
+ blk_offset = cur_offset;
+ blk_len = cur_length;
+ blk_ptr = cur_ptr;
+
+ /* Copy entire block */
+ while (blk_len) {
+ copied = bcma_sflash_read(cc,
+ blk_offset,
+ blk_len, blk_ptr);
+ blk_offset += copied;
+ blk_len -= copied;
+ blk_ptr += copied;
+ }
+ }
+
+ /* Copy input data into holding block */
+ memcpy(cur_ptr + (offset & mask), buf, cur_retlen);
+ }
+
+ /* Erase block */
+ ret = bcma_sflash_erase(cc, cur_offset);
+ if (ret < 0)
+ goto done;
+
+ while (bcma_sflash_poll(cc, cur_offset));
+
+ /* buf == NULL means erase only */
+ if (!buf) {
+ offset += cur_retlen;
+ len -= cur_retlen;
+ continue;
+ }
+
+ /* Write holding block */
+ while (cur_length > 0) {
+ bytes = bcma_sflash_write(cc, cur_offset,
+ cur_length, cur_ptr);
+
+ if (bytes < 0) {
+ ret = bytes;
+ goto done;
+ }
+
+ while (bcma_sflash_poll(cc, cur_offset))
+ ;
+
+ cur_offset += bytes;
+ cur_length -= bytes;
+ cur_ptr += bytes;
+ }
+
+ offset += cur_retlen;
+ len -= cur_retlen;
+ buf += cur_retlen;
+ }
+
+ ret = len;
+done:
+ kfree(block);
+ return ret;
+}
--- 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
@@ -375,6 +375,7 @@ struct bcma_chipcommon_pmu {
#ifdef CONFIG_BCMA_DRIVER_MIPS
enum bcma_flash_type {
BCMA_PFLASH,
+ BCMA_SFLASH,
};
struct bcma_pflash {
@@ -383,6 +384,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;
@@ -405,6 +414,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;
@@ -459,4 +471,16 @@ 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);
+int bcma_sflash_commit(struct bcma_drv_cc *cc, u32 offset, u32 len,
+ const u8 *buf);
+#endif /* CONFIG_BCMA_SFLASH */
+
#endif /* LINUX_BCMA_DRIVER_CC_H_ */

View File

@ -0,0 +1,149 @@
From e8afde87ecf56beff67c7d5371cabaa4fc018541 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sat, 23 Jul 2011 23:57:06 +0200
Subject: [PATCH 14/26] ssb: move flash to chipcommon
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
arch/mips/bcm47xx/nvram.c | 8 +++---
arch/mips/bcm47xx/wgt634u.c | 8 +++---
drivers/ssb/driver_mipscore.c | 36 +++++++++++++++++++++-------
include/linux/ssb/ssb_driver_chipcommon.h | 18 ++++++++++++++
include/linux/ssb/ssb_driver_mips.h | 4 ---
5 files changed, 53 insertions(+), 21 deletions(-)
--- 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);

View File

@ -0,0 +1,697 @@
From 980da78179592a3f5f99168bc5af415835aa8c13 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sun, 24 Jul 2011 20:20:36 +0200
Subject: [PATCH 15/26] ssb: add serial flash support
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
drivers/ssb/Kconfig | 6 +
drivers/ssb/Makefile | 1 +
drivers/ssb/driver_chipcommon_sflash.c | 556 +++++++++++++++++++++++++++++
drivers/ssb/driver_mipscore.c | 6 +
drivers/ssb/ssb_private.h | 4 +
include/linux/ssb/ssb_driver_chipcommon.h | 30 ++-
6 files changed, 601 insertions(+), 2 deletions(-)
create mode 100644 drivers/ssb/driver_chipcommon_sflash.c
--- 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,556 @@
+/*
+ * Broadcom SiliconBackplane chipcommon serial flash interface
+ *
+ * Copyright 2011, Jonas Gorski <jonas.gorski@gmail.com>
+ * Copyright 2010, Broadcom Corporation
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/ssb/ssb.h>
+#include <linux/ssb/ssb_driver_chipcommon.h>
+#include <linux/delay.h>
+
+#include "ssb_private.h"
+
+#define NUM_RETRIES 3
+
+
+/* Issue a serial flash command */
+static inline void ssb_sflash_cmd(struct ssb_chipcommon *cc, u32 opcode)
+{
+ chipco_write32(cc, SSB_CHIPCO_FLASHCTL,
+ SSB_CHIPCO_FLASHCTL_START | opcode);
+ while (chipco_read32(cc, SSB_CHIPCO_FLASHCTL)
+ & SSB_CHIPCO_FLASHCTL_BUSY)
+ ;
+}
+
+
+static inline void ssb_sflash_write_u8(struct ssb_chipcommon *cc,
+ u32 offset, u8 byte)
+{
+ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, offset);
+ chipco_write32(cc, SSB_CHIPCO_FLASHDATA, byte);
+}
+
+/* Initialize serial flash access */
+int ssb_sflash_init(struct ssb_chipcommon *cc)
+{
+ u32 id, id2;
+
+ memset(&cc->sflash, 0, sizeof(struct ssb_sflash));
+
+ switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) {
+ case SSB_CHIPCO_FLASHT_STSER:
+ /* Probe for ST chips */
+ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_DP);
+ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, 0);
+ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_RES);
+ id = chipco_read32(cc, SSB_CHIPCO_FLASHDATA);
+ cc->sflash.blocksize = 64 * 1024;
+ switch (id) {
+ case 0x11:
+ /* ST M25P20 2 Mbit Serial Flash */
+ cc->sflash.numblocks = 4;
+ break;
+ case 0x12:
+ /* ST M25P40 4 Mbit Serial Flash */
+ cc->sflash.numblocks = 8;
+ break;
+ case 0x13:
+ /* ST M25P80 8 Mbit Serial Flash */
+ cc->sflash.numblocks = 16;
+ break;
+ case 0x14:
+ /* ST M25P16 16 Mbit Serial Flash */
+ cc->sflash.numblocks = 32;
+ break;
+ case 0x15:
+ /* ST M25P32 32 Mbit Serial Flash */
+ cc->sflash.numblocks = 64;
+ break;
+ case 0x16:
+ /* ST M25P64 64 Mbit Serial Flash */
+ cc->sflash.numblocks = 128;
+ break;
+ case 0x17:
+ /* ST M25FL128 128 Mbit Serial Flash */
+ cc->sflash.numblocks = 256;
+ break;
+ case 0xbf:
+ /* All of the following flashes are SST with
+ * 4KB subsectors. Others should be added but
+ * We'll have to revamp the way we identify them
+ * since RES is not eough to disambiguate them.
+ */
+ cc->sflash.blocksize = 4 * 1024;
+ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, 1);
+ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_RES);
+ id2 = chipco_read32(cc, SSB_CHIPCO_FLASHDATA);
+ switch (id2) {
+ case 1:
+ /* SST25WF512 512 Kbit Serial Flash */
+ case 0x48:
+ /* SST25VF512 512 Kbit Serial Flash */
+ cc->sflash.numblocks = 16;
+ break;
+ case 2:
+ /* SST25WF010 1 Mbit Serial Flash */
+ case 0x49:
+ /* SST25VF010 1 Mbit Serial Flash */
+ cc->sflash.numblocks = 32;
+ break;
+ case 3:
+ /* SST25WF020 2 Mbit Serial Flash */
+ case 0x43:
+ /* SST25VF020 2 Mbit Serial Flash */
+ cc->sflash.numblocks = 64;
+ break;
+ case 4:
+ /* SST25WF040 4 Mbit Serial Flash */
+ case 0x44:
+ /* SST25VF040 4 Mbit Serial Flash */
+ case 0x8d:
+ /* SST25VF040B 4 Mbit Serial Flash */
+ cc->sflash.numblocks = 128;
+ break;
+ case 5:
+ /* SST25WF080 8 Mbit Serial Flash */
+ case 0x8e:
+ /* SST25VF080B 8 Mbit Serial Flash */
+ cc->sflash.numblocks = 256;
+ break;
+ case 0x41:
+ /* SST25VF016 16 Mbit Serial Flash */
+ cc->sflash.numblocks = 512;
+ break;
+ case 0x4a:
+ /* SST25VF032 32 Mbit Serial Flash */
+ cc->sflash.numblocks = 1024;
+ break;
+ case 0x4b:
+ /* SST25VF064 64 Mbit Serial Flash */
+ cc->sflash.numblocks = 2048;
+ break;
+ }
+ break;
+ }
+ break;
+
+ case SSB_CHIPCO_FLASHT_ATSER:
+ /* Probe for Atmel chips */
+ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_STATUS);
+ id = chipco_read32(cc, SSB_CHIPCO_FLASHDATA) & 0x3c;
+ switch (id) {
+ case 0xc:
+ /* Atmel AT45DB011 1Mbit Serial Flash */
+ cc->sflash.blocksize = 256;
+ cc->sflash.numblocks = 512;
+ break;
+ case 0x14:
+ /* Atmel AT45DB021 2Mbit Serial Flash */
+ cc->sflash.blocksize = 256;
+ cc->sflash.numblocks = 1024;
+ break;
+ case 0x1c:
+ /* Atmel AT45DB041 4Mbit Serial Flash */
+ cc->sflash.blocksize = 256;
+ cc->sflash.numblocks = 2048;
+ break;
+ case 0x24:
+ /* Atmel AT45DB081 8Mbit Serial Flash */
+ cc->sflash.blocksize = 256;
+ cc->sflash.numblocks = 4096;
+ break;
+ case 0x2c:
+ /* Atmel AT45DB161 16Mbit Serial Flash */
+ cc->sflash.blocksize = 512;
+ cc->sflash.numblocks = 4096;
+ break;
+ case 0x34:
+ /* Atmel AT45DB321 32Mbit Serial Flash */
+ cc->sflash.blocksize = 512;
+ cc->sflash.numblocks = 8192;
+ break;
+ case 0x3c:
+ /* Atmel AT45DB642 64Mbit Serial Flash */
+ cc->sflash.blocksize = 1024;
+ cc->sflash.numblocks = 8192;
+ break;
+ }
+ break;
+ }
+
+ cc->sflash.size = cc->sflash.blocksize * cc->sflash.numblocks;
+
+ return cc->sflash.size ? 0 : -ENODEV;
+}
+
+/* Read len bytes starting at offset into buf. Returns number of bytes read. */
+int ssb_sflash_read(struct ssb_chipcommon *cc, u32 offset, u32 len,
+ u8 *buf)
+{
+ u8 *from, *to;
+ u32 cnt, i;
+
+ if (!len)
+ return 0;
+
+ if ((offset + len) > cc->sflash.size)
+ return -EINVAL;
+
+ if ((len >= 4) && (offset & 3))
+ cnt = 4 - (offset & 3);
+ else if ((len >= 4) && ((u32)buf & 3))
+ cnt = 4 - ((u32)buf & 3);
+ else
+ cnt = len;
+
+
+ if (cc->dev->id.revision == 12)
+ from = (u8 *)KSEG1ADDR(SSB_FLASH2 + offset);
+ else
+ from = (u8 *)KSEG0ADDR(SSB_FLASH2 + offset);
+
+ to = (u8 *)buf;
+
+ if (cnt < 4) {
+ for (i = 0; i < cnt; i++) {
+ *to = readb(from);
+ from++;
+ to++;
+ }
+ return cnt;
+ }
+
+ while (cnt >= 4) {
+ *(u32 *)to = readl(from);
+ from += 4;
+ to += 4;
+ cnt -= 4;
+ }
+
+ return len - cnt;
+}
+
+/* Poll for command completion. Returns zero when complete. */
+int ssb_sflash_poll(struct ssb_chipcommon *cc, u32 offset)
+{
+ if (offset >= cc->sflash.size)
+ return -22;
+
+ switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) {
+ case SSB_CHIPCO_FLASHT_STSER:
+ /* Check for ST Write In Progress bit */
+ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_RDSR);
+ return chipco_read32(cc, SSB_CHIPCO_FLASHDATA)
+ & SSB_CHIPCO_FLASHSTA_ST_WIP;
+ case SSB_CHIPCO_FLASHT_ATSER:
+ /* Check for Atmel Ready bit */
+ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_STATUS);
+ return !(chipco_read32(cc, SSB_CHIPCO_FLASHDATA)
+ & SSB_CHIPCO_FLASHSTA_AT_READY);
+ }
+
+ return 0;
+}
+
+
+static int sflash_st_write(struct ssb_chipcommon *cc, u32 offset, u32 len,
+ const u8 *buf)
+{
+ struct ssb_bus *bus = cc->dev->bus;
+ int ret = 0;
+ bool is4712b0 = (bus->chip_id == 0x4712) && (bus->chip_rev == 3);
+ u32 mask;
+
+
+ /* Enable writes */
+ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_WREN);
+ if (is4712b0) {
+ mask = 1 << 14;
+ ssb_sflash_write_u8(cc, offset, *buf++);
+ /* Set chip select */
+ chipco_set32(cc, SSB_CHIPCO_GPIOOUT, mask);
+ /* Issue a page program with the first byte */
+ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_PP);
+ ret = 1;
+ offset++;
+ len--;
+ while (len > 0) {
+ if ((offset & 255) == 0) {
+ /* Page boundary, drop cs and return */
+ chipco_mask32(cc, SSB_CHIPCO_GPIOOUT, ~mask);
+ udelay(1);
+ if (!ssb_sflash_poll(cc, offset)) {
+ /* Flash rejected command */
+ return -EAGAIN;
+ }
+ return ret;
+ } else {
+ /* Write single byte */
+ ssb_sflash_cmd(cc, *buf++);
+ }
+ ret++;
+ offset++;
+ len--;
+ }
+ /* All done, drop cs */
+ chipco_mask32(cc, SSB_CHIPCO_GPIOOUT, ~mask);
+ udelay(1);
+ if (!ssb_sflash_poll(cc, offset)) {
+ /* Flash rejected command */
+ return -EAGAIN;
+ }
+ } else if (cc->dev->id.revision >= 20) {
+ ssb_sflash_write_u8(cc, offset, *buf++);
+ /* Issue a page program with CSA bit set */
+ ssb_sflash_cmd(cc,
+ SSB_CHIPCO_FLASHCTL_ST_CSA |
+ SSB_CHIPCO_FLASHCTL_ST_PP);
+ ret = 1;
+ offset++;
+ len--;
+ while (len > 0) {
+ if ((offset & 255) == 0) {
+ /* Page boundary, poll droping cs and return */
+ chipco_write32(cc, SSB_CHIPCO_FLASHCTL, 0);
+ udelay(1);
+ if (!ssb_sflash_poll(cc, offset)) {
+ /* Flash rejected command */
+ return -EAGAIN;
+ }
+ return ret;
+ } else {
+ /* Write single byte */
+ ssb_sflash_cmd(cc,
+ SSB_CHIPCO_FLASHCTL_ST_CSA |
+ *buf++);
+ }
+ ret++;
+ offset++;
+ len--;
+ }
+ /* All done, drop cs & poll */
+ chipco_write32(cc, SSB_CHIPCO_FLASHCTL, 0);
+ udelay(1);
+ if (!ssb_sflash_poll(cc, offset)) {
+ /* Flash rejected command */
+ return -EAGAIN;
+ }
+ } else {
+ ret = 1;
+ ssb_sflash_write_u8(cc, offset, *buf);
+ /* Page program */
+ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_PP);
+ }
+ return ret;
+}
+
+static int sflash_at_write(struct ssb_chipcommon *cc, u32 offset, u32 len,
+ const u8 *buf)
+{
+ struct ssb_sflash *sfl = &cc->sflash;
+ u32 page, byte, mask;
+ int ret = 0;
+ mask = sfl->blocksize - 1;
+ page = (offset & ~mask) << 1;
+ byte = offset & mask;
+ /* Read main memory page into buffer 1 */
+ if (byte || (len < sfl->blocksize)) {
+ int i = 100;
+ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, page);
+ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_BUF1_LOAD);
+ /* 250 us for AT45DB321B */
+ while (i > 0 && ssb_sflash_poll(cc, offset)) {
+ udelay(10);
+ i--;
+ }
+ BUG_ON(!ssb_sflash_poll(cc, offset));
+ }
+ /* Write into buffer 1 */
+ for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) {
+ ssb_sflash_write_u8(cc, byte++, *buf++);
+ ssb_sflash_cmd(cc,
+ SSB_CHIPCO_FLASHCTL_AT_BUF1_WRITE);
+ }
+ /* Write buffer 1 into main memory page */
+ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, page);
+ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_BUF1_PROGRAM);
+
+ return ret;
+}
+
+/* Write len bytes starting at offset into buf. Returns number of bytes
+ * written. Caller should poll for completion.
+ */
+int ssb_sflash_write(struct ssb_chipcommon *cc, u32 offset, u32 len,
+ const u8 *buf)
+{
+ struct ssb_sflash *sfl;
+ int ret = 0, tries = NUM_RETRIES;
+
+ if (!len)
+ return 0;
+
+ if ((offset + len) > cc->sflash.size)
+ return -EINVAL;
+
+ sfl = &cc->sflash;
+ switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) {
+ case SSB_CHIPCO_FLASHT_STSER:
+ do {
+ ret = sflash_st_write(cc, offset, len, buf);
+ tries--;
+ } while (ret == -EAGAIN && tries > 0);
+
+ if (ret == -EAGAIN && tries == 0) {
+ pr_info("ST Flash rejected write\n");
+ ret = -EIO;
+ }
+ break;
+ case SSB_CHIPCO_FLASHT_ATSER:
+ ret = sflash_at_write(cc, offset, len, buf);
+ break;
+ }
+
+ return ret;
+}
+
+/* Erase a region. Returns number of bytes scheduled for erasure.
+ * Caller should poll for completion.
+ */
+int ssb_sflash_erase(struct ssb_chipcommon *cc, u32 offset)
+{
+ struct ssb_sflash *sfl;
+
+ if (offset >= cc->sflash.size)
+ return -EINVAL;
+
+ sfl = &cc->sflash;
+ switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) {
+ case SSB_CHIPCO_FLASHT_STSER:
+ ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_WREN);
+ chipco_write32(cc, SSB_CHIPCO_FLASHADDR, offset);
+ /* Newer flashes have "sub-sectors" which can be erased independently
+ * with a new command: ST_SSE. The ST_SE command erases 64KB just as
+ * before.
+ */
+ ssb_sflash_cmd(cc, (sfl->blocksize < (64 * 1024)) ? SSB_CHIPCO_FLASHCTL_ST_SSE : 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;
+}
+
+/*
+ * writes the appropriate range of flash, a NULL buf simply erases
+ * the region of flash
+ */
+int ssb_sflash_commit(struct ssb_chipcommon *cc, u32 offset, u32 len,
+ const u8 *buf)
+{
+ struct ssb_sflash *sfl;
+ u8 *block = NULL, *cur_ptr, *blk_ptr;
+ u32 blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder;
+ u32 blk_offset, blk_len, copied;
+ int bytes, ret = 0;
+
+ /* Check address range */
+ if (len <= 0)
+ return 0;
+
+ sfl = &cc->sflash;
+ if ((offset + len) > sfl->size)
+ return -EINVAL;
+
+ blocksize = sfl->blocksize;
+ mask = blocksize - 1;
+
+ /* Allocate a block of mem */
+ block = kmalloc(blocksize, GFP_KERNEL);
+ if (!block)
+ return -ENOMEM;
+
+ while (len) {
+ /* Align offset */
+ cur_offset = offset & ~mask;
+ cur_length = blocksize;
+ cur_ptr = block;
+
+ remainder = blocksize - (offset & mask);
+ if (len < remainder)
+ cur_retlen = len;
+ else
+ cur_retlen = remainder;
+
+ /* buf == NULL means erase only */
+ if (buf) {
+ /* Copy existing data into holding block if necessary */
+ if ((offset & mask) || (len < blocksize)) {
+ blk_offset = cur_offset;
+ blk_len = cur_length;
+ blk_ptr = cur_ptr;
+
+ /* Copy entire block */
+ while (blk_len) {
+ copied = ssb_sflash_read(cc,
+ blk_offset,
+ blk_len, blk_ptr);
+ blk_offset += copied;
+ blk_len -= copied;
+ blk_ptr += copied;
+ }
+ }
+
+ /* Copy input data into holding block */
+ memcpy(cur_ptr + (offset & mask), buf, cur_retlen);
+ }
+
+ /* Erase block */
+ ret = ssb_sflash_erase(cc, cur_offset);
+ if (ret < 0)
+ goto done;
+
+ while (ssb_sflash_poll(cc, cur_offset));
+
+ /* buf == NULL means erase only */
+ if (!buf) {
+ offset += cur_retlen;
+ len -= cur_retlen;
+ continue;
+ }
+
+ /* Write holding block */
+ while (cur_length > 0) {
+ bytes = ssb_sflash_write(cc, cur_offset,
+ cur_length, cur_ptr);
+
+ if (bytes < 0) {
+ ret = bytes;
+ goto done;
+ }
+
+ while (ssb_sflash_poll(cc, cur_offset))
+ ;
+
+ cur_offset += bytes;
+ cur_length -= bytes;
+ cur_ptr += bytes;
+ }
+
+ offset += cur_retlen;
+ len -= cur_retlen;
+ buf += cur_retlen;
+ }
+
+ ret = len;
+done:
+ kfree(block);
+ return ret;
+}
--- 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,18 @@ 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);
+int ssb_sflash_commit(struct ssb_chipcommon *cc, u32 offset, u32 len,
+ const u8 *buf);
+#endif /* CONFIG_SSB_SFLASH */
+
/* PMU support */
extern void ssb_pmu_init(struct ssb_chipcommon *cc);

View File

@ -0,0 +1,193 @@
From 4f314ac9edbc80897f158fdb4e1b1de8a2d0d432 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sun, 24 Jul 2011 21:10:49 +0200
Subject: [PATCH 16/26] brcm47xx: add common interface for sflash
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
arch/mips/bcm47xx/Makefile | 2 +-
arch/mips/bcm47xx/bus.c | 94 ++++++++++++++++++++++++++++++
arch/mips/bcm47xx/setup.c | 8 +++
arch/mips/include/asm/mach-bcm47xx/bus.h | 37 ++++++++++++
4 files changed, 140 insertions(+), 1 deletions(-)
create mode 100644 arch/mips/bcm47xx/bus.c
create mode 100644 arch/mips/include/asm/mach-bcm47xx/bus.h
--- 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,94 @@
+/*
+ * BCM947xx nvram variable access
+ *
+ * Copyright (C) 2011 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <bus.h>
+
+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);
+}
+
+static int bcm47xx_sflash_bcma_commit(struct bcm47xx_sflash *dev, u32 offset, u32 len, const u8 *buf)
+{
+ return bcma_sflash_commit(dev->bcc, offset, len, buf);
+}
+
+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->commit = bcm47xx_sflash_bcma_commit;
+
+ 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);
+}
+
+static int bcm47xx_sflash_ssb_commit(struct bcm47xx_sflash *dev, u32 offset, u32 len, const u8 *buf)
+{
+ return ssb_sflash_commit(dev->scc, offset, len, buf);
+}
+
+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->commit = bcm47xx_sflash_ssb_commit;
+
+ 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)\n", 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)\n", 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,37 @@
+/*
+ * BCM947xx nvram variable access
+ *
+ * Copyright (C) 2011 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/ssb/ssb.h>
+#include <linux/bcma/bcma.h>
+#include <bcm47xx.h>
+
+struct bcm47xx_sflash {
+ enum bcm47xx_bus_type sflash_type;
+ union {
+ struct ssb_chipcommon *scc;
+ struct bcma_drv_cc *bcc;
+ };
+
+ int (*read)(struct bcm47xx_sflash *dev, u32 offset, u32 len, u8 *buf);
+ int (*poll)(struct bcm47xx_sflash *dev, u32 offset);
+ int (*write)(struct bcm47xx_sflash *dev, u32 offset, u32 len, const u8 *buf);
+ int (*erase)(struct bcm47xx_sflash *dev, u32 offset);
+ int (*commit)(struct bcm47xx_sflash *dev, u32 offset, u32 len, const u8 *buf);
+
+ 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;

View File

@ -0,0 +1,585 @@
From d50d2d8e3ab5446f791deff0cb78820989ed93e7 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sun, 17 Jul 2011 14:54:11 +0200
Subject: [PATCH 06/19] mtd: bcm47xx: add bcm47xx part parser
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
drivers/mtd/Kconfig | 7 +
drivers/mtd/Makefile | 1 +
drivers/mtd/bcm47xxpart.c | 542 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 550 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/bcm47xxpart.c
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -164,6 +164,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
@@ -12,6 +12,7 @@ obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdli
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o
+obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
# 'Users' - code which presents functionality to userspace.
obj-$(CONFIG_MTD_CHAR) += mtdchar.o
--- /dev/null
+++ b/drivers/mtd/bcm47xxpart.c
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2005 Waldemar Brodkorb <wbx@openwrt.org>
+ * Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org)
+ *
+ * original functions for finding root filesystem from Mike Baker
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Copyright 2001-2003, Broadcom Corporation
+ * All Rights Reserved.
+ *
+ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
+ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
+ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
+ *
+ * Flash mapping for BCM947XX boards
+ */
+
+#define pr_fmt(fmt) "bcm47xx_part: " fmt
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/crc32.h>
+#include <linux/io.h>
+#include <asm/mach-bcm47xx/nvram.h>
+#include <asm/mach-bcm47xx/bcm47xx.h>
+#include <asm/fw/cfe/cfe_api.h>
+
+
+#define TRX_MAGIC 0x30524448 /* "HDR0" */
+#define TRX_VERSION 1
+#define TRX_MAX_LEN 0x3A0000
+#define TRX_NO_HEADER 1 /* Do not write TRX header */
+#define TRX_GZ_FILES 0x2 /* Contains up to TRX_MAX_OFFSET individual gzip files */
+#define TRX_MAX_OFFSET 3
+
+struct trx_header {
+ u32 magic; /* "HDR0" */
+ u32 len; /* Length of file including header */
+ u32 crc32; /* 32-bit CRC from flag_version to end of file */
+ u32 flag_version; /* 0:15 flags, 16:31 version */
+ u32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of header */
+};
+
+/* for Edimax Print servers which use an additional header
+ * then the firmware on flash looks like :
+ * EDIMAX HEADER | TRX HEADER
+ * As this header is 12 bytes long we have to handle it
+ * and skip it to find the TRX header
+ */
+#define EDIMAX_PS_HEADER_MAGIC 0x36315350 /* "PS16" */
+#define EDIMAX_PS_HEADER_LEN 0xc /* 12 bytes long for edimax header */
+
+#define NVRAM_SPACE 0x8000
+
+#define ROUTER_NETGEAR_WGR614L 1
+#define ROUTER_NETGEAR_WNR834B 2
+#define ROUTER_NETGEAR_WNDR3300 3
+#define ROUTER_NETGEAR_WNR3500L 4
+#define ROUTER_SIMPLETECH_SIMPLESHARE 5
+#define ROUTER_NETGEAR_WNDR3400 6
+
+static int
+find_cfe_size(struct mtd_info *mtd)
+{
+ struct trx_header *trx;
+ unsigned char buf[512];
+ int off;
+ size_t len;
+ int blocksize;
+
+ trx = (struct trx_header *) buf;
+
+ blocksize = mtd->erasesize;
+ if (blocksize < 0x10000)
+ blocksize = 0x10000;
+
+ for (off = (128*1024); off < mtd->size; off += blocksize) {
+ memset(buf, 0xe5, sizeof(buf));
+
+ /*
+ * Read into buffer
+ */
+ if (mtd->read(mtd, off, sizeof(buf), &len, buf) ||
+ len != sizeof(buf))
+ continue;
+
+ if (le32_to_cpu(trx->magic) == EDIMAX_PS_HEADER_MAGIC) {
+ if (mtd->read(mtd, off + EDIMAX_PS_HEADER_LEN,
+ sizeof(buf), &len, buf) || len != sizeof(buf)) {
+ continue;
+ } else {
+ pr_notice("Found edimax header\n");
+ }
+ }
+
+ /* found a TRX header */
+ if (le32_to_cpu(trx->magic) == TRX_MAGIC)
+ goto found;
+ }
+
+ pr_notice("%s: Couldn't find bootloader size\n", mtd->name);
+ return -1;
+
+ found:
+ pr_notice("bootloader size: %d\n", off);
+ return off;
+
+}
+
+/*
+ * Copied from mtdblock.c
+ *
+ * Cache stuff...
+ *
+ * Since typical flash erasable sectors are much larger than what Linux's
+ * buffer cache can handle, we must implement read-modify-write on flash
+ * sectors for each block write requests. To avoid over-erasing flash sectors
+ * and to speed things up, we locally cache a whole flash sector while it is
+ * being written to until a different sector is required.
+ */
+
+static void erase_callback(struct erase_info *done)
+{
+ wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
+ wake_up(wait_q);
+}
+
+static int erase_write(struct mtd_info *mtd, unsigned long pos,
+ int len, const char *buf)
+{
+ struct erase_info erase;
+ DECLARE_WAITQUEUE(wait, current);
+ wait_queue_head_t wait_q;
+ size_t retlen;
+ int ret;
+
+ /*
+ * First, let's erase the flash block.
+ */
+
+ init_waitqueue_head(&wait_q);
+ erase.mtd = mtd;
+ erase.callback = erase_callback;
+ erase.addr = pos;
+ erase.len = len;
+ erase.priv = (u_long)&wait_q;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&wait_q, &wait);
+
+ ret = mtd->erase(mtd, &erase);
+ if (ret) {
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&wait_q, &wait);
+ pr_warn("erase of region [0x%lx, 0x%x] on \"%s\" failed\n",
+ pos, len, mtd->name);
+ return ret;
+ }
+
+ schedule(); /* Wait for erase to finish. */
+ remove_wait_queue(&wait_q, &wait);
+
+ /*
+ * Next, write data to flash.
+ */
+
+ ret = mtd->write(mtd, pos, len, &retlen, buf);
+ if (ret)
+ return ret;
+ if (retlen != len)
+ return -EIO;
+ return 0;
+}
+
+
+static int
+find_dual_image_off(struct mtd_info *mtd)
+{
+ struct trx_header trx;
+ int off, blocksize;
+ size_t len;
+
+ blocksize = mtd->erasesize;
+ if (blocksize < 0x10000)
+ blocksize = 0x10000;
+
+ for (off = (128*1024); off < mtd->size; off += blocksize) {
+ memset(&trx, 0xe5, sizeof(trx));
+ /*
+ * Read into buffer
+ */
+ if (mtd->read(mtd, off, sizeof(trx), &len, (char *) &trx) ||
+ len != sizeof(trx))
+ continue;
+ /* found last TRX header */
+ if (le32_to_cpu(trx.magic) == TRX_MAGIC) {
+ if (le32_to_cpu(trx.flag_version >> 16) == 2) {
+ pr_notice("dual image TRX header found\n");
+ return mtd->size / 2;
+ } else {
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+
+static int
+find_root(struct mtd_info *mtd, struct mtd_partition *part)
+{
+ struct trx_header trx, *trx2;
+ unsigned char buf[512], *block;
+ int off, blocksize, trxoff = 0;
+ u32 i, crc = ~0;
+ size_t len;
+ bool edimax = false;
+
+ blocksize = mtd->erasesize;
+ if (blocksize < 0x10000)
+ blocksize = 0x10000;
+
+ for (off = (128*1024); off < mtd->size; off += blocksize) {
+ memset(&trx, 0xe5, sizeof(trx));
+
+ /*
+ * Read into buffer
+ */
+ if (mtd->read(mtd, off, sizeof(trx), &len, (char *) &trx) ||
+ len != sizeof(trx))
+ continue;
+
+ /* found an edimax header */
+ if (le32_to_cpu(trx.magic) == EDIMAX_PS_HEADER_MAGIC) {
+ /* read the correct trx header */
+ if (mtd->read(mtd, off + EDIMAX_PS_HEADER_LEN,
+ sizeof(trx), &len, (char *) &trx) ||
+ len != sizeof(trx)) {
+ continue;
+ } else {
+ pr_notice("Found an edimax ps header\n");
+ edimax = true;
+ }
+ }
+
+ /* found a TRX header */
+ if (le32_to_cpu(trx.magic) == TRX_MAGIC) {
+ part->offset = le32_to_cpu(trx.offsets[2]) ? :
+ le32_to_cpu(trx.offsets[1]);
+ part->size = le32_to_cpu(trx.len);
+
+ part->size -= part->offset;
+ part->offset += off;
+ if (edimax) {
+ off += EDIMAX_PS_HEADER_LEN;
+ trxoff = EDIMAX_PS_HEADER_LEN;
+ }
+
+ goto found;
+ }
+ }
+
+ pr_warn("%s: Couldn't find root filesystem\n",
+ mtd->name);
+ return -1;
+
+ found:
+ pr_notice("TRX offset : %x\n", trxoff);
+ if (part->size == 0)
+ return 0;
+
+ if (mtd->read(mtd, part->offset, sizeof(buf), &len, buf) || len != sizeof(buf))
+ return 0;
+
+ /* Move the fs outside of the trx */
+ part->size = 0;
+
+ if (trx.len != part->offset + part->size - off) {
+ /* Update the trx offsets and length */
+ trx.len = part->offset + part->size - off;
+
+ /* Update the trx crc32 */
+ for (i = (u32) &(((struct trx_header *)NULL)->flag_version); i <= trx.len; i += sizeof(buf)) {
+ if (mtd->read(mtd, off + i, sizeof(buf), &len, buf) || len != sizeof(buf))
+ return 0;
+ crc = crc32_le(crc, buf, min(sizeof(buf), trx.len - i));
+ }
+ trx.crc32 = crc;
+
+ /* read first eraseblock from the trx */
+ block = kmalloc(mtd->erasesize, GFP_KERNEL);
+ trx2 = (struct trx_header *) block;
+ if (mtd->read(mtd, off - trxoff, mtd->erasesize, &len, block) || len != mtd->erasesize) {
+ pr_err("Error accessing the first trx eraseblock\n");
+ return 0;
+ }
+
+ pr_notice("Updating TRX offsets and length:\n");
+ pr_notice("old trx = [0x%08x, 0x%08x, 0x%08x], len=0x%08x crc32=0x%08x\n", trx2->offsets[0], trx2->offsets[1], trx2->offsets[2], trx2->len, trx2->crc32);
+ pr_notice("new trx = [0x%08x, 0x%08x, 0x%08x], len=0x%08x crc32=0x%08x\n", trx.offsets[0], trx.offsets[1], trx.offsets[2], trx.len, trx.crc32);
+
+ /* Write updated trx header to the flash */
+ memcpy(block + trxoff, &trx, sizeof(trx));
+ if (mtd->unlock)
+ mtd->unlock(mtd, off - trxoff, mtd->erasesize);
+ erase_write(mtd, off - trxoff, mtd->erasesize, block);
+ if (mtd->sync)
+ mtd->sync(mtd);
+ kfree(block);
+ pr_notice("Done\n");
+ }
+
+ return part->size;
+}
+
+static int get_router(void)
+{
+ char buf[20];
+ u32 boardnum = 0;
+ u16 boardtype = 0;
+ u16 boardrev = 0;
+ u32 boardflags = 0;
+ u16 sdram_init = 0;
+ u16 cardbus = 0;
+ u16 strev = 0;
+
+ if (nvram_getenv("boardnum", buf, sizeof(buf)) >= 0)
+ boardnum = simple_strtoul(buf, NULL, 0);
+ if (nvram_getenv("boardtype", buf, sizeof(buf)) >= 0)
+ boardtype = simple_strtoul(buf, NULL, 0);
+ if (nvram_getenv("boardrev", buf, sizeof(buf)) >= 0)
+ boardrev = simple_strtoul(buf, NULL, 0);
+ if (nvram_getenv("boardflags", buf, sizeof(buf)) >= 0)
+ boardflags = simple_strtoul(buf, NULL, 0);
+ if (nvram_getenv("sdram_init", buf, sizeof(buf)) >= 0)
+ sdram_init = simple_strtoul(buf, NULL, 0);
+ if (nvram_getenv("cardbus", buf, sizeof(buf)) >= 0)
+ cardbus = simple_strtoul(buf, NULL, 0);
+ if (nvram_getenv("st_rev", buf, sizeof(buf)) >= 0)
+ strev = simple_strtoul(buf, NULL, 0);
+
+ if ((boardnum == 8 || boardnum == 01)
+ && boardtype == 0x0472 && cardbus == 1) {
+ /* Netgear WNR834B, Netgear WNR834Bv2 */
+ return ROUTER_NETGEAR_WNR834B;
+ }
+
+ if (boardnum == 01 && boardtype == 0x0472 && boardrev == 0x23) {
+ /* Netgear WNDR-3300 */
+ return ROUTER_NETGEAR_WNDR3300;
+ }
+
+ if ((boardnum == 83258 || boardnum == 01)
+ && boardtype == 0x048e
+ && (boardrev == 0x11 || boardrev == 0x10)
+ && boardflags == 0x750
+ && sdram_init == 0x000A) {
+ /* Netgear WGR614v8/L/WW 16MB ram, cfe v1.3 or v1.5 */
+ return ROUTER_NETGEAR_WGR614L;
+ }
+
+ if ((boardnum == 1 || boardnum == 3500)
+ && boardtype == 0x04CF
+ && (boardrev == 0x1213 || boardrev == 02)) {
+ /* Netgear WNR3500v2/U/L */
+ return ROUTER_NETGEAR_WNR3500L;
+ }
+
+ if (boardnum == 1 && boardtype == 0xb4cf && boardrev == 0x1100) {
+ /* Netgear WNDR3400 */
+ return ROUTER_NETGEAR_WNDR3400;
+ }
+
+ if (boardtype == 0x042f
+ && boardrev == 0x10
+ && boardflags == 0
+ && strev == 0x11) {
+ /* Simpletech Simpleshare */
+ return ROUTER_SIMPLETECH_SIMPLESHARE;
+ }
+
+ return 0;
+}
+
+static int parse_bcm47xx_partitions(struct mtd_info *mtd,
+ struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+{
+ int cfe_size;
+ int dual_image_offset = 0;
+ /* e.g Netgear 0x003e0000-0x003f0000 : "board_data", we exclude this
+ * part from our mapping to prevent overwriting len/checksum on e.g.
+ * Netgear WGR614v8/L/WW
+ */
+ int custom_data_size = 0;
+ struct mtd_partition *bcm47xx_parts;
+
+ cfe_size = find_cfe_size(mtd);
+ if (cfe_size < 0)
+ return 0;
+
+ bcm47xx_parts = kzalloc(sizeof(struct mtd_partition) * 6, GFP_KERNEL);
+
+ bcm47xx_parts[0].name = "cfe";
+ bcm47xx_parts[1].name = "linux";
+ bcm47xx_parts[2].name = "rootfs";
+ bcm47xx_parts[3].name = "nvram";
+
+ /* boot loader */
+ bcm47xx_parts[0].mask_flags = MTD_WRITEABLE;
+ bcm47xx_parts[0].offset = 0;
+ bcm47xx_parts[0].size = cfe_size;
+
+ /* nvram */
+ if (cfe_size != 384 * 1024) {
+
+ switch (get_router()) {
+ case ROUTER_NETGEAR_WGR614L:
+ case ROUTER_NETGEAR_WNR834B:
+ case ROUTER_NETGEAR_WNDR3300:
+ case ROUTER_NETGEAR_WNR3500L:
+ case ROUTER_NETGEAR_WNDR3400:
+ /* Netgear: checksum is @ 0x003AFFF8 for 4M flash or checksum
+ * is @ 0x007AFFF8 for 8M flash
+ */
+ custom_data_size = mtd->erasesize;
+
+ bcm47xx_parts[3].offset = mtd->size - roundup(NVRAM_SPACE, mtd->erasesize);
+ bcm47xx_parts[3].size = roundup(NVRAM_SPACE, mtd->erasesize);
+
+ /* Place CFE board_data into a partition */
+ bcm47xx_parts[4].name = "board_data";
+ bcm47xx_parts[4].offset = bcm47xx_parts[3].offset - custom_data_size;
+ bcm47xx_parts[4].size = custom_data_size;
+ break;
+
+ case ROUTER_SIMPLETECH_SIMPLESHARE:
+ /* Fixup Simpletech Simple share nvram */
+
+ pr_notice("Setting up simpletech nvram\n");
+ custom_data_size = mtd->erasesize;
+
+ bcm47xx_parts[3].offset = mtd->size - roundup(NVRAM_SPACE, mtd->erasesize) * 2;
+ bcm47xx_parts[3].size = roundup(NVRAM_SPACE, mtd->erasesize);
+
+ /* Place backup nvram into a partition */
+ bcm47xx_parts[4].name = "nvram_copy";
+ bcm47xx_parts[4].offset = mtd->size - roundup(NVRAM_SPACE, mtd->erasesize);
+ bcm47xx_parts[4].size = roundup(NVRAM_SPACE, mtd->erasesize);
+ break;
+
+ default:
+ bcm47xx_parts[3].offset = mtd->size - roundup(NVRAM_SPACE, mtd->erasesize);
+ bcm47xx_parts[3].size = roundup(NVRAM_SPACE, mtd->erasesize);
+ }
+
+ } else {
+ /* nvram (old 128kb config partition on netgear wgt634u) */
+ bcm47xx_parts[3].offset = bcm47xx_parts[0].size;
+ bcm47xx_parts[3].size = roundup(NVRAM_SPACE, mtd->erasesize);
+ }
+
+ /* dual image offset*/
+ pr_notice("Looking for dual image\n");
+ dual_image_offset = find_dual_image_off(mtd);
+ /* linux (kernel and rootfs) */
+ if (cfe_size != 384 * 1024) {
+ if (get_router() == ROUTER_SIMPLETECH_SIMPLESHARE) {
+ bcm47xx_parts[1].offset = bcm47xx_parts[0].size;
+ bcm47xx_parts[1].size = bcm47xx_parts[4].offset - dual_image_offset -
+ bcm47xx_parts[1].offset - custom_data_size;
+ } else {
+ bcm47xx_parts[1].offset = bcm47xx_parts[0].size;
+ bcm47xx_parts[1].size = bcm47xx_parts[3].offset - dual_image_offset -
+ bcm47xx_parts[1].offset - custom_data_size;
+ }
+ } else {
+ /* do not count the elf loader, which is on one block */
+ bcm47xx_parts[1].offset = bcm47xx_parts[0].size +
+ bcm47xx_parts[3].size + mtd->erasesize;
+ bcm47xx_parts[1].size = mtd->size -
+ bcm47xx_parts[0].size -
+ (2*bcm47xx_parts[3].size) -
+ mtd->erasesize - custom_data_size;
+ }
+
+ /* find and size rootfs */
+ find_root(mtd, &bcm47xx_parts[2]);
+ bcm47xx_parts[2].size = mtd->size - dual_image_offset -
+ bcm47xx_parts[2].offset -
+ bcm47xx_parts[3].size - custom_data_size;
+ *pparts = bcm47xx_parts;
+ return bcm47xx_parts[4].name == NULL ? 4 : 5;
+}
+
+static struct mtd_part_parser bcm47xx_parser = {
+ .owner = THIS_MODULE,
+ .parse_fn = parse_bcm47xx_partitions,
+ .name = "bcm47xx",
+};
+
+static int __init bcm47xx_parser_init(void)
+{
+ return register_mtd_parser(&bcm47xx_parser);
+}
+
+static void __exit bcm47xx_parser_exit(void)
+{
+ deregister_mtd_parser(&bcm47xx_parser);
+}
+
+module_init(bcm47xx_parser_init);
+module_exit(bcm47xx_parser_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parsing code for flash partitions on bcm47xx SoCs");

View File

@ -0,0 +1,230 @@
From 36f8b899174a445a98fe02ed8d1db177525f0c52 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sun, 17 Jul 2011 14:55:18 +0200
Subject: [PATCH 07/15] mtd: bcm47xx: add parallel flash driver
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
drivers/mtd/maps/Kconfig | 9 ++
drivers/mtd/maps/Makefile | 1 +
drivers/mtd/maps/bcm47xx-pflash.c | 188 +++++++++++++++++++++++++++++++++++++
3 files changed, 198 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/maps/bcm47xx-pflash.c
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -257,6 +257,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
@@ -58,3 +58,4 @@ obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr
obj-$(CONFIG_MTD_BCM963XX) += bcm963xx-flash.o
obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o
obj-$(CONFIG_MTD_LANTIQ) += lantiq-flash.o
+obj-$(CONFIG_MTD_BCM47XX_PFLASH)+= bcm47xx-pflash.o
--- /dev/null
+++ b/drivers/mtd/maps/bcm47xx-pflash.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2005 Waldemar Brodkorb <wbx@openwrt.org>
+ * Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org)
+ *
+ * original functions for finding root filesystem from Mike Baker
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Copyright 2001-2003, Broadcom Corporation
+ * All Rights Reserved.
+ *
+ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
+ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
+ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
+ *
+ * Flash mapping for BCM947XX boards
+ */
+
+#define pr_fmt(fmt) "bcm47xx_pflash: " fmt
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/io.h>
+#include <asm/mach-bcm47xx/bcm47xx.h>
+#include <linux/platform_device.h>
+
+#define WINDOW_ADDR 0x1fc00000
+#define WINDOW_SIZE 0x400000
+#define BUSWIDTH 2
+
+static struct mtd_info *bcm47xx_mtd;
+
+static void bcm47xx_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+{
+ if (len == 1) {
+ memcpy_fromio(to, map->virt + from, len);
+ } else {
+ int i;
+ u16 *dest = (u16 *) to;
+ u16 *src = (u16 *) (map->virt + from);
+ for (i = 0; i < (len / 2); i++)
+ dest[i] = src[i];
+ if (len & 1)
+ *((u8 *)dest+len-1) = src[i] & 0xff;
+ }
+}
+
+static struct map_info bcm47xx_map = {
+ name: "Physically mapped flash",
+ size : WINDOW_SIZE,
+ bankwidth : BUSWIDTH,
+ phys : WINDOW_ADDR,
+};
+
+static const char *probes[] = { "bcm47xx", NULL };
+
+static int bcm47xx_mtd_probe(struct platform_device *pdev)
+{
+#ifdef CONFIG_BCM47XX_SSB
+ struct ssb_chipcommon *ssb_cc;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+ struct bcma_drv_cc *bcma_cc;
+#endif
+ int ret = 0;
+
+ switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+ case BCM47XX_BUS_TYPE_SSB:
+ ssb_cc = &bcm47xx_bus.ssb.chipco;
+ if (ssb_cc->flash_type != SSB_PFLASH)
+ return -ENODEV;
+
+ bcm47xx_map.phys = ssb_cc->pflash.window;
+ bcm47xx_map.size = ssb_cc->pflash.window_size;
+ bcm47xx_map.bankwidth = ssb_cc->pflash.buswidth;
+ break;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+ case BCM47XX_BUS_TYPE_BCMA:
+ bcma_cc = &bcm47xx_bus.bcma.bus.drv_cc;
+ if (bcma_cc->flash_type != BCMA_PFLASH)
+ return -ENODEV;
+
+ bcm47xx_map.phys = bcma_cc->pflash.window;
+ bcm47xx_map.size = bcma_cc->pflash.window_size;
+ bcm47xx_map.bankwidth = bcma_cc->pflash.buswidth;
+ break;
+#endif
+ }
+
+ pr_notice("flash init: 0x%08x 0x%08lx\n", bcm47xx_map.phys, bcm47xx_map.size);
+ bcm47xx_map.virt = ioremap_nocache(bcm47xx_map.phys, bcm47xx_map.size);
+
+ if (!bcm47xx_map.virt) {
+ pr_err("Failed to ioremap\n");
+ return -EIO;
+ }
+
+ simple_map_init(&bcm47xx_map);
+ /* override copy_from routine */
+ bcm47xx_map.copy_from = bcm47xx_map_copy_from;
+
+ bcm47xx_mtd = do_map_probe("cfi_probe", &bcm47xx_map);
+ if (!bcm47xx_mtd) {
+ pr_err("Failed to do_map_probe\n");
+ ret = -ENXIO;
+ goto err_unmap;
+ }
+ bcm47xx_mtd->owner = THIS_MODULE;
+
+ pr_notice("Flash device: 0x%lx at 0x%x\n", bcm47xx_map.size, WINDOW_ADDR);
+
+ ret = mtd_device_parse_register(bcm47xx_mtd, probes, NULL, NULL, 0);
+
+ if (ret) {
+ pr_err("Flash: mtd_device_register failed\n");
+ goto err_destroy;
+ }
+ return 0;
+
+err_destroy:
+ map_destroy(bcm47xx_mtd);
+err_unmap:
+ iounmap(bcm47xx_map.virt);
+ return ret;
+}
+
+static int __devexit bcm47xx_mtd_remove(struct platform_device *pdev)
+{
+ mtd_device_unregister(bcm47xx_mtd);
+ map_destroy(bcm47xx_mtd);
+ iounmap(bcm47xx_map.virt);
+ return 0;
+}
+
+static struct platform_driver bcm47xx_mtd_driver = {
+ .remove = __devexit_p(bcm47xx_mtd_remove),
+ .driver = {
+ .name = "bcm47xx_pflash",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init init_bcm47xx_mtd(void)
+{
+ int ret = platform_driver_probe(&bcm47xx_mtd_driver, bcm47xx_mtd_probe);
+
+ if (ret)
+ pr_err("error registering platform driver: %i\n", ret);
+ return ret;
+}
+
+static void __exit exit_bcm47xx_mtd(void)
+{
+ platform_driver_unregister(&bcm47xx_mtd_driver);
+}
+
+module_init(init_bcm47xx_mtd);
+module_exit(exit_bcm47xx_mtd);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("BCM47XX parallel flash driver");

View File

@ -0,0 +1,315 @@
From 2e2951220bf63e05449c03a95453680da1029e44 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sun, 17 Jul 2011 14:55:45 +0200
Subject: [PATCH 08/15] mtd: bcm47xx: add serial flash driver
sflash get the sflash ops from platform device
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
arch/mips/include/asm/mach-bcm47xx/bus.h | 3 +
drivers/mtd/maps/Kconfig | 9 +
drivers/mtd/maps/Makefile | 1 +
drivers/mtd/maps/bcm47xx-sflash.c | 252 ++++++++++++++++++++++++++++++
4 files changed, 265 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/maps/bcm47xx-sflash.c
--- a/arch/mips/include/asm/mach-bcm47xx/bus.h
+++ b/arch/mips/include/asm/mach-bcm47xx/bus.h
@@ -11,6 +11,7 @@
#include <linux/ssb/ssb.h>
#include <linux/bcma/bcma.h>
+#include <linux/mtd/mtd.h>
#include <bcm47xx.h>
struct bcm47xx_sflash {
@@ -29,6 +30,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
@@ -266,6 +266,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
@@ -59,3 +59,4 @@ obj-$(CONFIG_MTD_BCM963XX) += bcm963xx-f
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,252 @@
+/*
+ * Broadcom SiliconBackplane chipcommon serial flash interface
+ *
+ * Copyright 2006, 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.
+ *
+ * $Id$
+ */
+
+#define pr_fmt(fmt) "bcm47xx_sflash: " fmt
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <bcm47xx.h>
+#include <bus.h>
+
+static int
+sflash_mtd_poll(struct bcm47xx_sflash *sflash, unsigned int offset, int timeout)
+{
+ unsigned long now = jiffies;
+ int ret = 0;
+
+ for (;;) {
+ if (!sflash->poll(sflash, offset)) {
+ ret = 0;
+ break;
+ }
+ if (time_after(jiffies, now + timeout)) {
+ pr_err("timeout while polling\n");
+ ret = -ETIMEDOUT;
+ break;
+ }
+ udelay(1);
+ }
+
+ return ret;
+}
+
+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)
+{
+ 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) {
+ int bytes;
+ int 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 *regions;
+ int ret = 0;
+
+ mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
+ if (!mtd)
+ return -ENOMEM;
+
+ regions = kzalloc(sizeof(struct mtd_erase_region_info), GFP_KERNEL);
+ if (!mtd)
+ return -ENOMEM;
+
+ pr_info("found serial flash: blocksize=%dKB, numblocks=%d, size=%dKB\n",
+ sflash->blocksize/1024, sflash->numblocks, sflash->size / 1024);
+
+ /* Setup region info */
+ regions->offset = 0;
+ regions->erasesize = sflash->blocksize;
+ regions->numblocks = sflash->numblocks;
+ if (regions->erasesize > mtd->erasesize)
+ mtd->erasesize = regions->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 = regions;
+ mtd->erase = sflash_mtd_erase;
+ mtd->read = sflash_mtd_read;
+ mtd->write = sflash_mtd_write;
+ mtd->writesize = 1;
+ mtd->priv = sflash;
+ mtd->owner = THIS_MODULE;
+
+ ret = mtd_device_parse_register(mtd, probes, NULL, NULL, 0);
+
+ if (ret) {
+ pr_err("mtd_device_register failed\n");
+ return ret;
+ }
+ sflash->mtd = mtd;
+ return 0;
+}
+
+static int __devexit bcm47xx_sflash_remove(struct platform_device *pdev)
+{
+ struct bcm47xx_sflash *sflash = dev_get_platdata(&pdev->dev);
+
+ if (sflash) {
+ mtd_device_unregister(sflash->mtd);
+ map_destroy(sflash->mtd);
+ kfree(sflash->mtd->eraseregions);
+ kfree(sflash->mtd);
+ }
+ 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");

View File

@ -0,0 +1,100 @@
From 64f3d068654589d6114048ac5933cd4498706cfc Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sun, 17 Jul 2011 15:02:10 +0200
Subject: [PATCH 20/26] bcm47xx: register flash drivers
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
arch/mips/bcm47xx/setup.c | 72 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 72 insertions(+), 0 deletions(-)
--- a/arch/mips/bcm47xx/setup.c
+++ b/arch/mips/bcm47xx/setup.c
@@ -31,10 +31,12 @@
#include <linux/ssb/ssb.h>
#include <linux/ssb/ssb_embedded.h>
#include <linux/bcma/bcma_soc.h>
+#include <linux/platform_device.h>
#include <asm/bootinfo.h>
#include <asm/reboot.h>
#include <asm/time.h>
#include <bcm47xx.h>
+#include <bus.h>
#include <asm/mach-bcm47xx/nvram.h>
union bcm47xx_bus bcm47xx_bus;
@@ -366,3 +368,73 @@ static int __init bcm47xx_register_bus_c
return 0;
}
device_initcall(bcm47xx_register_bus_complete);
+
+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 int __init bcm47xx_register_flash(void)
+{
+#ifdef CONFIG_BCM47XX_SSB
+ struct ssb_chipcommon *chipco;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+ struct bcma_drv_cc *drv_cc;
+#endif
+ switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+ case BCM47XX_BUS_TYPE_SSB:
+ chipco = &bcm47xx_bus.ssb.chipco;
+ if (chipco->flash_type == 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);
+ } else if (chipco->flash_type == SSB_SFLASH) {
+ bcm47xx_sflash_dev.dev.platform_data = &bcm47xx_sflash;
+ return platform_device_register(&bcm47xx_sflash_dev);
+ } else {
+ printk(KERN_ERR "No flash device found\n");
+ return -1;
+ }
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+ case BCM47XX_BUS_TYPE_BCMA:
+ drv_cc = &bcm47xx_bus.bcma.bus.drv_cc;
+ if (drv_cc->flash_type == 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);
+ } else if (drv_cc->flash_type == BCMA_SFLASH) {
+ bcm47xx_sflash_dev.dev.platform_data = &bcm47xx_sflash;
+ return platform_device_register(&bcm47xx_sflash_dev);
+ } else {
+ printk(KERN_ERR "No flash device found\n");
+ return -1;
+ }
+#endif
+ }
+ return -1;
+}
+fs_initcall(bcm47xx_register_flash);

View File

@ -0,0 +1,33 @@
From e6730c06cfc827d715f43e9bd276ae939bb86af9 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Fri, 22 Jul 2011 17:11:51 +0200
Subject: [PATCH 23/26] bcma: use randoom mac address as long as reading it out does not work
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
drivers/bcma/sprom.c | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
--- a/drivers/bcma/sprom.c
+++ b/drivers/bcma/sprom.c
@@ -13,6 +13,7 @@
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
+#include <linux/etherdevice.h>
#define SPOFF(offset) ((offset) / sizeof(u16))
@@ -214,8 +215,10 @@ int bcma_sprom_get(struct bcma_bus *bus)
if (!bus->drv_cc.core)
return -EOPNOTSUPP;
- if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM))
+ if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM)) {
+ random_ether_addr(bus->sprom.il0mac);
return -ENOENT;
+ }
sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
GFP_KERNEL);

View File

@ -0,0 +1,118 @@
From 1d693b2c9d5943cbe938f879041b837cd004737f Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sat, 23 Jul 2011 18:29:38 +0200
Subject: [PATCH 25/26] bcm47xx: read nvram from sflash
bcm47xx: add sflash support to nvram
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
arch/mips/bcm47xx/nvram.c | 86 +++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 84 insertions(+), 2 deletions(-)
--- a/arch/mips/bcm47xx/nvram.c
+++ b/arch/mips/bcm47xx/nvram.c
@@ -20,11 +20,12 @@
#include <asm/addrspace.h>
#include <asm/mach-bcm47xx/nvram.h>
#include <asm/mach-bcm47xx/bcm47xx.h>
+#include <asm/mach-bcm47xx/bus.h>
static char nvram_buf[NVRAM_SPACE];
/* Probe for NVRAM header */
-static void early_nvram_init(void)
+static void early_nvram_init_pflash(void)
{
#ifdef CONFIG_BCM47XX_SSB
struct ssb_chipcommon *ssb_cc;
@@ -86,7 +87,88 @@ 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;
+}
+
+static void early_nvram_init(void)
+{
+ int err = 0;
+
+ switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+ case BCM47XX_BUS_TYPE_SSB:
+ if (bcm47xx_bus.ssb.chipco.flash_type == SSB_PFLASH) {
+ early_nvram_init_pflash();
+ } else if (bcm47xx_bus.ssb.chipco.flash_type == SSB_SFLASH) {
+ err = early_nvram_init_sflash();
+ if (err < 0)
+ printk(KERN_WARNING "can not read from flash: %i\n", err);
+ } else {
+ printk(KERN_WARNING "unknow flash type\n");
+ }
+ break;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+ case BCM47XX_BUS_TYPE_BCMA:
+ if (bcm47xx_bus.bcma.bus.drv_cc.flash_type == BCMA_PFLASH) {
+ early_nvram_init_pflash();
+ } else if (bcm47xx_bus.bcma.bus.drv_cc.flash_type == BCMA_SFLASH) {
+ err = early_nvram_init_sflash();
+ if (err < 0)
+ printk(KERN_WARNING "can not read from flash: %i\n", err);
+ } else {
+ printk(KERN_WARNING "unknow flash type\n");
+ }
+ break;
+#endif
+ }
}
int nvram_getenv(char *name, char *val, size_t val_len)

View File

@ -0,0 +1,47 @@
From f6e41db3ee7ead99e1398def222c14893fc265de Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Thu, 4 Aug 2011 21:09:48 +0200
Subject: [PATCH 26/26] bcma: export needed gpio functions
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
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)

View File

@ -0,0 +1,60 @@
From 1735daf1db79d338dccfc55444b52ed52af79e86 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sun, 20 Nov 2011 18:22:35 +0100
Subject: [PATCH 15/21] bcma: scan for extra address space
Some cores like the USB core have two address spaces. In the USB host
controller one address space is used for the OHCI and the other for the
EHCI controller interface. The USB controller is the only core I found
with two address spaces. This code is based on the AI scan function
ai_scan() in shared/aiutils.c i the Broadcom SDK.
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
drivers/bcma/scan.c | 17 ++++++++++++++++-
include/linux/bcma/bcma.h | 1 +
2 files changed, 17 insertions(+), 1 deletions(-)
--- a/drivers/bcma/scan.c
+++ b/drivers/bcma/scan.c
@@ -286,6 +286,21 @@ 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) {
+ printk("found bridge\n");
+ }
+
+ }
+ core->addr = tmp;
+
/* get & parse slave ports */
for (i = 0; i < ports[1]; i++) {
for (j = 0; ; j++) {
@@ -298,7 +313,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
@@ -138,6 +138,7 @@ struct bcma_device {
u8 core_index;
u32 addr;
+ u32 addr1;
u32 wrap;
void __iomem *io_addr;

View File

@ -0,0 +1,115 @@
From 6e8ae6e2cee0e7e5939dc7042584c808366e61e0 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sun, 27 Nov 2011 14:01:01 +0100
Subject: [PATCH 16/21] =?UTF-8?q?bcma:=20add=20function=20to=20check=20every?=
=?UTF-8?q?=2010=20=C2=B5s=20if=20a=20reg=20is=20set?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This function checks if a reg get set or cleared every 10 microseconds.
It is used in bcma_core_set_clockmode() and bcma_core_pll_ctl() to
reduce code duplication. In addition it is needed in the USB host
driver.
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
drivers/bcma/core.c | 48 ++++++++++++++++++++++++++++----------------
include/linux/bcma/bcma.h | 2 +
2 files changed, 32 insertions(+), 18 deletions(-)
--- a/drivers/bcma/core.c
+++ b/drivers/bcma/core.c
@@ -52,11 +52,36 @@ int bcma_core_enable(struct bcma_device
}
EXPORT_SYMBOL_GPL(bcma_core_enable);
+/* Wait for bitmask in a register to get set or cleared.
+ * timeout is in units of ten-microseconds.
+ */
+int bcma_wait_bits(struct bcma_device *dev, u16 reg, u32 bitmask, int timeout,
+ int set)
+{
+ int i;
+ u32 val;
+
+ for (i = 0; i < timeout; i++) {
+ val = bcma_read32(dev, reg);
+ if (set) {
+ if ((val & bitmask) == bitmask)
+ return 0;
+ } else {
+ if (!(val & bitmask))
+ return 0;
+ }
+ udelay(10);
+ }
+ pr_err("Timeout waiting for bitmask %08X on register %04X to %s.\n",
+ bitmask, reg, (set ? "set" : "clear"));
+
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_GPL(bcma_wait_bits);
+
void bcma_core_set_clockmode(struct bcma_device *core,
enum bcma_clkmode clkmode)
{
- u16 i;
-
WARN_ON(core->id.id != BCMA_CORE_CHIPCOMMON &&
core->id.id != BCMA_CORE_PCIE &&
core->id.id != BCMA_CORE_80211);
@@ -65,15 +90,8 @@ void bcma_core_set_clockmode(struct bcma
case BCMA_CLKMODE_FAST:
bcma_set32(core, BCMA_CLKCTLST, BCMA_CLKCTLST_FORCEHT);
udelay(64);
- for (i = 0; i < 1500; i++) {
- if (bcma_read32(core, BCMA_CLKCTLST) &
- BCMA_CLKCTLST_HAVEHT) {
- i = 0;
- break;
- }
- udelay(10);
- }
- if (i)
+ if (bcma_wait_bits(core, BCMA_CLKCTLST, BCMA_CLKCTLST_HAVEHT,
+ 1500, 1))
pr_err("HT force timeout\n");
break;
case BCMA_CLKMODE_DYNAMIC:
@@ -85,22 +103,12 @@ EXPORT_SYMBOL_GPL(bcma_core_set_clockmod
void bcma_core_pll_ctl(struct bcma_device *core, u32 req, u32 status, bool on)
{
- u16 i;
-
WARN_ON(req & ~BCMA_CLKCTLST_EXTRESREQ);
WARN_ON(status & ~BCMA_CLKCTLST_EXTRESST);
if (on) {
bcma_set32(core, BCMA_CLKCTLST, req);
- for (i = 0; i < 10000; i++) {
- if ((bcma_read32(core, BCMA_CLKCTLST) & status) ==
- status) {
- i = 0;
- break;
- }
- udelay(10);
- }
- if (i)
+ if (bcma_wait_bits(core, BCMA_CLKCTLST, status, 10000, 1))
pr_err("PLL enable timeout\n");
} else {
pr_warn("Disabling PLL not supported yet!\n");
--- a/include/linux/bcma/bcma.h
+++ b/include/linux/bcma/bcma.h
@@ -283,6 +283,9 @@ static inline void bcma_maskset16(struct
bcma_write16(cc, offset, (bcma_read16(cc, offset) & mask) | set);
}
+extern int bcma_wait_bits(struct bcma_device *dev, u16 reg, u32 bitmask,
+ int timeout, int set);
+
extern bool bcma_core_is_enabled(struct bcma_device *core);
extern void bcma_core_disable(struct bcma_device *core, u32 flags);
extern int bcma_core_enable(struct bcma_device *core, u32 flags);

View File

@ -0,0 +1,291 @@
From 14297ee700e1c2e04a35466304f9a3c5e1387146 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sat, 26 Nov 2011 21:20:54 +0100
Subject: [PATCH 17/30] USB: OHCI: Add a generic platform device driver
This adds a generic driver for platform devices. It works like the PCI
driver and is based on it. This is for devices which do not have an own
bus but their OHCI controller works like a PCI controller. It will be
used for the Broadcom bcma and ssb USB OHCI controller.
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
drivers/usb/host/Kconfig | 10 ++
drivers/usb/host/ohci-hcd.c | 21 ++++-
drivers/usb/host/ohci-platform.c | 197 ++++++++++++++++++++++++++++++++++++++
3 files changed, 227 insertions(+), 1 deletions(-)
create mode 100644 drivers/usb/host/ohci-platform.c
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -378,6 +378,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 "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
@@ -1116,6 +1116,11 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ohci_xls_driver
#endif
+#ifdef CONFIG_USB_OHCI_HCD_PLATFORM
+#include "ohci-platform.c"
+#define PLATFORM_OHCI_DRIVER ohci_platform_driver
+#endif
+
#if !defined(PCI_DRIVER) && \
!defined(PLATFORM_DRIVER) && \
!defined(OMAP1_PLATFORM_DRIVER) && \
@@ -1125,7 +1130,8 @@ MODULE_LICENSE ("GPL");
!defined(PS3_SYSTEM_BUS_DRIVER) && \
!defined(SM501_OHCI_DRIVER) && \
!defined(TMIO_OHCI_DRIVER) && \
- !defined(SSB_OHCI_DRIVER)
+ !defined(SSB_OHCI_DRIVER) && \
+ !defined(PLATFORM_OHCI_DRIVER)
#error "missing bus glue for ohci-hcd"
#endif
@@ -1209,9 +1215,19 @@ static int __init ohci_hcd_mod_init(void
goto error_tmio;
#endif
+#ifdef PLATFORM_OHCI_DRIVER
+ retval = platform_driver_register(&PLATFORM_OHCI_DRIVER);
+ if (retval)
+ goto error_platform;
+#endif
+
return retval;
/* Error path */
+#ifdef PLATFORM_OHCI_DRIVER
+ platform_driver_unregister(&PLATFORM_OHCI_DRIVER);
+ error_platform:
+#endif
#ifdef TMIO_OHCI_DRIVER
platform_driver_unregister(&TMIO_OHCI_DRIVER);
error_tmio:
@@ -1265,6 +1281,9 @@ module_init(ohci_hcd_mod_init);
static void __exit ohci_hcd_mod_exit(void)
{
+#ifdef PLATFORM_OHCI_DRIVER
+ platform_driver_unregister(&PLATFORM_OHCI_DRIVER);
+#endif
#ifdef TMIO_OHCI_DRIVER
platform_driver_unregister(&TMIO_OHCI_DRIVER);
#endif
--- /dev/null
+++ b/drivers/usb/host/ohci-platform.c
@@ -0,0 +1,197 @@
+/*
+ * Generic platform ohci driver
+ *
+ * Copyright 2007 Michael Buesch <m@bues.ch>
+ * Copyright 2011 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Derived from the OHCI-PCI driver
+ * Copyright 1999 Roman Weissgaerber
+ * Copyright 2000-2002 David Brownell
+ * Copyright 1999 Linus Torvalds
+ * Copyright 1999 Gregory P. Smith
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+#include <linux/platform_device.h>
+
+static int ohci_platform_reset(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int err;
+
+ 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 = "platform-usb-ohci",
+ .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 ohci_platform_attach(struct platform_device *dev)
+{
+ struct usb_hcd *hcd;
+ struct resource *res_irq, *res_mem;
+ int err = -ENOMEM;
+
+ hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev,
+ dev_name(&dev->dev));
+ if (!hcd)
+ goto err_return;
+
+ res_irq = platform_get_resource(dev, IORESOURCE_IRQ, 0);
+ if (!res_irq) {
+ err = -ENXIO;
+ goto err_return;
+ }
+ res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!res_mem) {
+ err = -ENXIO;
+ goto err_return;
+ }
+ hcd->rsrc_start = res_mem->start;
+ hcd->rsrc_len = res_mem->end - res_mem->start + 1;
+
+ hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs)
+ goto err_put_hcd;
+ err = usb_add_hcd(hcd, res_irq->start, IRQF_SHARED);
+ if (err)
+ goto err_iounmap;
+
+ platform_set_drvdata(dev, hcd);
+
+ return err;
+
+err_iounmap:
+ iounmap(hcd->regs);
+err_put_hcd:
+ usb_put_hcd(hcd);
+err_return:
+ return err;
+}
+
+static int ohci_platform_probe(struct platform_device *dev)
+{
+ int err;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ /* We currently always attach BCMA_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 = ohci_platform_attach(dev);
+
+ return err;
+}
+
+static int ohci_platform_remove(struct platform_device *dev)
+{
+ struct usb_hcd *hcd;
+
+ hcd = platform_get_drvdata(dev);
+ if (!hcd)
+ return -ENODEV;
+
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static void ohci_platform_shutdown(struct platform_device *dev)
+{
+ struct usb_hcd *hcd;
+
+ hcd = platform_get_drvdata(dev);
+ if (!hcd)
+ return;
+
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
+}
+
+#ifdef CONFIG_PM
+
+static int ohci_platform_suspend(struct platform_device *dev,
+ pm_message_t state)
+{
+
+ return 0;
+}
+
+static int ohci_platform_resume(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_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 struct platform_driver ohci_platform_driver = {
+ .id_table = ohci_platform_table,
+ .probe = ohci_platform_probe,
+ .remove = ohci_platform_remove,
+ .shutdown = ohci_platform_shutdown,
+ .suspend = ohci_platform_suspend,
+ .resume = ohci_platform_resume,
+ .driver = {
+ .name = "ohci-platform",
+ }
+};

View File

@ -0,0 +1,265 @@
From 2ade1c32109d2f17fdfc0b414411d4ee7f828706 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sat, 26 Nov 2011 21:28:56 +0100
Subject: [PATCH 18/30] USB: EHCI: Add a generic platform device driver
This adds a generic driver for platform devices. It works like the PCI
driver and is based on it. This is for devices which do not have an own
bus but their EHCI controller works like a PCI controller. It will be
used for the Broadcom bcma and ssb USB EHCI controller.
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
drivers/usb/host/Kconfig | 10 ++
drivers/usb/host/ehci-hcd.c | 5 +
drivers/usb/host/ehci-platform.c | 211 ++++++++++++++++++++++++++++++++++++++
3 files changed, 226 insertions(+), 0 deletions(-)
create mode 100644 drivers/usb/host/ehci-platform.c
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -388,6 +388,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
@@ -1329,6 +1329,11 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ehci_xls_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,211 @@
+/*
+ * Generic platform ehci driver
+ *
+ * Copyright 2007 Steven Brown <sbrown@cortland.com>
+ * Copyright 2010-2011 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Derived from the ohci-ssb driver
+ * Copyright 2007 Michael Buesch <mb@bu3sch.de>
+ *
+ * Derived from the EHCI-PCI driver
+ * Copyright (c) 2000-2004 by David Brownell
+ *
+ * Derived from the ohci-pci driver
+ * Copyright 1999 Roman Weissgaerber
+ * Copyright 2000-2002 David Brownell
+ * Copyright 1999 Linus Torvalds
+ * Copyright 1999 Gregory P. Smith
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+#include <linux/platform_device.h>
+
+static int ehci_platform_reset(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+
+ ehci->caps = hcd->regs;
+ ehci->regs = hcd->regs +
+ HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
+
+ dbg_hcs_params(ehci, "reset");
+ dbg_hcc_params(ehci, "reset");
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+ retval = ehci_halt(ehci);
+ if (retval)
+ return retval;
+
+ /* data structure init */
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
+ ehci_reset(ehci);
+
+ ehci_port_power(ehci, 1);
+
+ return retval;
+}
+
+static const struct hc_driver ehci_platform_hc_driver = {
+ .description = "platform-usb-ehci",
+ .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 ehci_platform_attach(struct platform_device *dev)
+{
+ struct usb_hcd *hcd;
+ struct resource *res_irq, *res_mem;
+ int err = -ENOMEM;
+
+ hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
+ dev_name(&dev->dev));
+ if (!hcd)
+ goto err_return;
+
+ res_irq = platform_get_resource(dev, IORESOURCE_IRQ, 0);
+ if (!res_irq) {
+ err = -ENXIO;
+ goto err_return;
+ }
+ res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!res_mem) {
+ err = -ENXIO;
+ goto err_return;
+ }
+ hcd->rsrc_start = res_mem->start;
+ hcd->rsrc_len = res_mem->end - res_mem->start + 1;
+
+ /*
+ * start & size modified per sbutils.c
+ */
+ hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs)
+ goto err_put_hcd;
+ err = usb_add_hcd(hcd, res_irq->start, IRQF_SHARED);
+ if (err)
+ goto err_iounmap;
+
+ platform_set_drvdata(dev, hcd);
+
+ return err;
+
+err_iounmap:
+ iounmap(hcd->regs);
+err_put_hcd:
+ usb_put_hcd(hcd);
+err_return:
+ return err;
+}
+
+static int ehci_platform_probe(struct platform_device *dev)
+{
+ int err;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ err = ehci_platform_attach(dev);
+
+ return err;
+}
+
+static int ehci_platform_remove(struct platform_device *dev)
+{
+ struct usb_hcd *hcd;
+
+ hcd = platform_get_drvdata(dev);
+ if (!hcd)
+ return -ENODEV;
+
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static void ehci_platform_shutdown(struct platform_device *dev)
+{
+ struct usb_hcd *hcd;
+
+ hcd = platform_get_drvdata(dev);
+ if (!hcd)
+ return;
+
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
+}
+
+#ifdef CONFIG_PM
+
+static int ehci_platform_suspend(struct platform_device *dev,
+ pm_message_t state)
+{
+ return 0;
+}
+
+static int ehci_platform_resume(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+
+ ehci_finish_controller_resume(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 struct platform_driver ehci_platform_driver = {
+ .id_table = ehci_platform_table,
+ .probe = ehci_platform_probe,
+ .remove = ehci_platform_remove,
+ .shutdown = ehci_platform_shutdown,
+ .suspend = ehci_platform_suspend,
+ .resume = ehci_platform_resume,
+ .driver = {
+ .name = "ehci-platform",
+ }
+};

View File

@ -0,0 +1,358 @@
From 0da055d81f77272ebc41d4c2cd5f533aa9025cc7 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sat, 26 Nov 2011 21:33:41 +0100
Subject: [PATCH 19/30] USB: Add driver for the bcma bus
This adds a USB driver using the generic platform device driver for the
USB controller found on the Broadcom bcma bus. The bcma bus just
exposes one device which serves the OHCI and the EHCI controller at the
same time. This driver probes for this USB controller and creates and
registers two new platform devices which will be probed by the new
generic platform device driver. This makes it possible to use the EHCI
and the OCHI controller on the bcma bus at the same time.
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
drivers/usb/host/Kconfig | 12 ++
drivers/usb/host/Makefile | 1 +
drivers/usb/host/bcma-hcd.c | 309 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 322 insertions(+), 0 deletions(-)
create mode 100644 drivers/usb/host/bcma-hcd.c
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -618,3 +618,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,309 @@
+/*
+ * Broadcom specific Advanced Microcontroller Bus
+ * Broadcom USB-core driver (BCMA bus glue)
+ *
+ * Copyright 2011 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Based on ssb-ohci driver
+ * Copyright 2007 Michael Buesch <m@bues.ch>
+ *
+ * Derived from the OHCI-PCI driver
+ * Copyright 1999 Roman Weissgaerber
+ * Copyright 2000-2002 David Brownell
+ * Copyright 1999 Linus Torvalds
+ * Copyright 1999 Gregory P. Smith
+ *
+ * Derived from the USBcore related parts of Broadcom-SB
+ * Copyright 2005-2011 Broadcom Corporation
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+#include <linux/bcma/bcma.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+
+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;
+};
+
+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, 1))
+ 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, 1)) {
+ 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 struct platform_device * __devinit
+bcma_hcd_create_pdev(struct bcma_device *dev, char *name, 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(name, 0);
+ if (!hci_dev)
+ goto err_alloc;
+
+ 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;
+
+ ret = platform_device_add(hci_dev);
+ if (ret) {
+err_alloc:
+ platform_device_put(hci_dev);
+ return ERR_PTR(ret);
+ }
+
+ return hci_dev;
+}
+
+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, "ohci-platform",
+ 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, "ehci-platform",
+ 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;
+ struct platform_device *ohci_dev;
+ struct platform_device *ehci_dev;
+
+ usb_dev = bcma_get_drvdata(dev);
+ if (!usb_dev)
+ return;
+
+ ohci_dev = usb_dev->ohci_dev;
+ 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);

View File

@ -0,0 +1,325 @@
From ca1db834336ac19edc7a5d342f8acece1ae1feb9 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sat, 26 Nov 2011 21:35:17 +0100
Subject: [PATCH 20/30] USB: Add driver for the ssb bus
This adds a USB driver using the generic platform device driver for the
USB controller found on the Broadcom ssb bus. The ssb bus just
exposes one device which serves the OHCI and the EHCI controller at the
same time. This driver probes for this USB controller and creates and
registers two new platform devices which will be probed by the new
generic platform device driver. This makes it possible to use the EHCI
and the OCHI controller on the ssb bus at the same time.
The old ssb OHCI USB driver will be removed in the next step as this
driver also provide an OHCI driver and an EHCI for the cores supporting
it.
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
drivers/usb/host/Kconfig | 12 ++
drivers/usb/host/Makefile | 1 +
drivers/usb/host/ssb-hcd.c | 272 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 285 insertions(+), 0 deletions(-)
create mode 100644 drivers/usb/host/ssb-hcd.c
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -630,3 +630,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,272 @@
+/*
+ * Sonics Silicon Backplane
+ * Broadcom USB-core driver (SSB bus glue)
+ *
+ * Copyright 2011 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Based on ssb-ohci driver
+ * Copyright 2007 Michael Buesch <m@bues.ch>
+ *
+ * Derived from the OHCI-PCI driver
+ * Copyright 1999 Roman Weissgaerber
+ * Copyright 2000-2002 David Brownell
+ * Copyright 1999 Linus Torvalds
+ * Copyright 1999 Gregory P. Smith
+ *
+ * Derived from the USBcore related parts of Broadcom-SB
+ * Copyright 2005-2011 Broadcom Corporation
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+#include <linux/ssb/ssb.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+
+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 struct platform_device * __devinit
+ssb_hcd_create_pdev(struct ssb_device *dev, char *name, 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(name, 0);
+ if (!hci_dev)
+ goto err_alloc;
+
+ 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, 2);
+ if (ret)
+ goto err_alloc;
+
+ ret = platform_device_add(hci_dev);
+ if (ret) {
+err_alloc:
+ platform_device_put(hci_dev);
+ return ERR_PTR(ret);
+ }
+
+ return hci_dev;
+}
+
+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;
+ 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 = ssb_admatch_size(tmp);
+ usb_dev->ohci_dev = ssb_hcd_create_pdev(dev, "ohci-platform", start,
+ len);
+ if (IS_ERR(usb_dev->ohci_dev)) {
+ err = PTR_ERR(usb_dev->ohci_dev);
+ goto err_free_usb_dev;
+ }
+
+ if (dev->id.coreid == SSB_DEV_USB20_HOST) {
+ start = ssb_admatch_base(tmp) + 0x800; /* ehci core offset */
+ len = 0x100; /* ehci reg block size */
+ usb_dev->ehci_dev = ssb_hcd_create_pdev(dev, "ehci-platform",
+ 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;
+ struct platform_device *ohci_dev;
+ struct platform_device *ehci_dev;
+
+ usb_dev = ssb_get_drvdata(dev);
+ if (!usb_dev)
+ return;
+
+ ohci_dev = usb_dev->ohci_dev;
+ 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);

View File

@ -0,0 +1,357 @@
From 77190e21ed262397eb924e6d48e12ecf3e9f6316 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sat, 26 Nov 2011 21:36:50 +0100
Subject: [PATCH 21/30] USB: OHCI: remove old SSB OHCI driver
This is now replaced by the new ssb USB driver, which also supports
devices with an EHCI controller.
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
drivers/usb/host/Kconfig | 13 --
drivers/usb/host/ohci-hcd.c | 19 ---
drivers/usb/host/ohci-ssb.c | 260 -------------------------------------------
3 files changed, 0 insertions(+), 292 deletions(-)
delete mode 100644 drivers/usb/host/ohci-ssb.c
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -351,19 +351,6 @@ config USB_OHCI_HCD_PCI
Enables support for PCI-bus plug-in USB controller cards.
If unsure, say Y.
-config USB_OHCI_HCD_SSB
- bool "OHCI support for Broadcom SSB OHCI core"
- depends on USB_OHCI_HCD && (SSB = y || SSB = USB_OHCI_HCD) && EXPERIMENTAL
- default n
- ---help---
- Support for the Sonics Silicon Backplane (SSB) attached
- Broadcom USB OHCI core.
-
- This device is present in some embedded devices with
- Broadcom based SSB bus.
-
- If unsure, say N.
-
config USB_OHCI_SH
bool "OHCI support for SuperH USB controller"
depends on USB_OHCI_HCD && SUPERH
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1076,11 +1076,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
@@ -1130,7 +1125,6 @@ MODULE_LICENSE ("GPL");
!defined(PS3_SYSTEM_BUS_DRIVER) && \
!defined(SM501_OHCI_DRIVER) && \
!defined(TMIO_OHCI_DRIVER) && \
- !defined(SSB_OHCI_DRIVER) && \
!defined(PLATFORM_OHCI_DRIVER)
#error "missing bus glue for ohci-hcd"
#endif
@@ -1197,12 +1191,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)
@@ -1236,10 +1224,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:
@@ -1290,9 +1274,6 @@ static void __exit ohci_hcd_mod_exit(voi
#ifdef SM501_OHCI_DRIVER
platform_driver_unregister(&SM501_OHCI_DRIVER);
#endif
-#ifdef SSB_OHCI_DRIVER
- ssb_driver_unregister(&SSB_OHCI_DRIVER);
-#endif
#ifdef PCI_DRIVER
pci_unregister_driver(&PCI_DRIVER);
#endif
--- a/drivers/usb/host/ohci-ssb.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Sonics Silicon Backplane
- * Broadcom USB-core OHCI driver
- *
- * Copyright 2007 Michael Buesch <m@bues.ch>
- *
- * Derived from the OHCI-PCI driver
- * Copyright 1999 Roman Weissgaerber
- * Copyright 2000-2002 David Brownell
- * Copyright 1999 Linus Torvalds
- * Copyright 1999 Gregory P. Smith
- *
- * Derived from the USBcore related parts of Broadcom-SB
- * Copyright 2005 Broadcom Corporation
- *
- * Licensed under the GNU/GPL. See COPYING for details.
- */
-#include <linux/ssb/ssb.h>
-
-
-#define SSB_OHCI_TMSLOW_HOSTMODE (1 << 29)
-
-struct ssb_ohci_device {
- struct ohci_hcd ohci; /* _must_ be at the beginning. */
-
- u32 enable_flags;
-};
-
-static inline
-struct ssb_ohci_device *hcd_to_ssb_ohci(struct usb_hcd *hcd)
-{
- return (struct ssb_ohci_device *)(hcd->hcd_priv);
-}
-
-
-static int ssb_ohci_reset(struct usb_hcd *hcd)
-{
- struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
- struct ohci_hcd *ohci = &ohcidev->ohci;
- int err;
-
- ohci_hcd_init(ohci);
- err = ohci_init(ohci);
-
- return err;
-}
-
-static int ssb_ohci_start(struct usb_hcd *hcd)
-{
- struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
- struct ohci_hcd *ohci = &ohcidev->ohci;
- int err;
-
- err = ohci_run(ohci);
- if (err < 0) {
- ohci_err(ohci, "can't start\n");
- ohci_stop(hcd);
- }
-
- return err;
-}
-
-static const struct hc_driver ssb_ohci_hc_driver = {
- .description = "ssb-usb-ohci",
- .product_desc = "SSB OHCI Controller",
- .hcd_priv_size = sizeof(struct ssb_ohci_device),
-
- .irq = ohci_irq,
- .flags = HCD_MEMORY | HCD_USB11,
-
- .reset = ssb_ohci_reset,
- .start = ssb_ohci_start,
- .stop = ohci_stop,
- .shutdown = ohci_shutdown,
-
- .urb_enqueue = ohci_urb_enqueue,
- .urb_dequeue = ohci_urb_dequeue,
- .endpoint_disable = ohci_endpoint_disable,
-
- .get_frame_number = ohci_get_frame,
-
- .hub_status_data = ohci_hub_status_data,
- .hub_control = ohci_hub_control,
-#ifdef CONFIG_PM
- .bus_suspend = ohci_bus_suspend,
- .bus_resume = ohci_bus_resume,
-#endif
-
- .start_port_reset = ohci_start_port_reset,
-};
-
-static void ssb_ohci_detach(struct ssb_device *dev)
-{
- struct usb_hcd *hcd = ssb_get_drvdata(dev);
-
- if (hcd->driver->shutdown)
- hcd->driver->shutdown(hcd);
- usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
- usb_put_hcd(hcd);
- ssb_device_disable(dev, 0);
-}
-
-static int ssb_ohci_attach(struct ssb_device *dev)
-{
- struct ssb_ohci_device *ohcidev;
- struct usb_hcd *hcd;
- int err = -ENOMEM;
- u32 tmp, flags = 0;
-
- if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) ||
- dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32)))
- return -EOPNOTSUPP;
-
- if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) {
- /* Put the device into host-mode. */
- flags |= SSB_OHCI_TMSLOW_HOSTMODE;
- ssb_device_enable(dev, flags);
- } else if (dev->id.coreid == SSB_DEV_USB20_HOST) {
- /*
- * USB 2.0 special considerations:
- *
- * In addition to the standard SSB reset sequence, the Host
- * Control Register must be programmed to bring the USB core
- * and various phy components out of reset.
- */
- ssb_device_enable(dev, 0);
- ssb_write32(dev, 0x200, 0x7ff);
-
- /* Change Flush control reg */
- tmp = ssb_read32(dev, 0x400);
- tmp &= ~8;
- ssb_write32(dev, 0x400, tmp);
- tmp = ssb_read32(dev, 0x400);
-
- /* Change Shim control reg */
- tmp = ssb_read32(dev, 0x304);
- tmp &= ~0x100;
- ssb_write32(dev, 0x304, tmp);
- tmp = ssb_read32(dev, 0x304);
-
- udelay(1);
-
- /* Work around for 5354 failures */
- if (dev->id.revision == 2 && dev->bus->chip_id == 0x5354) {
- /* Change syn01 reg */
- tmp = 0x00fe00fe;
- ssb_write32(dev, 0x894, tmp);
-
- /* Change syn03 reg */
- tmp = ssb_read32(dev, 0x89c);
- tmp |= 0x1;
- ssb_write32(dev, 0x89c, tmp);
- }
- } else
- ssb_device_enable(dev, 0);
-
- hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev,
- dev_name(dev->dev));
- if (!hcd)
- goto err_dev_disable;
- ohcidev = hcd_to_ssb_ohci(hcd);
- ohcidev->enable_flags = flags;
-
- tmp = ssb_read32(dev, SSB_ADMATCH0);
- hcd->rsrc_start = ssb_admatch_base(tmp);
- hcd->rsrc_len = ssb_admatch_size(tmp);
- hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
- if (!hcd->regs)
- goto err_put_hcd;
- err = usb_add_hcd(hcd, dev->irq, IRQF_SHARED);
- if (err)
- goto err_iounmap;
-
- ssb_set_drvdata(dev, hcd);
-
- return err;
-
-err_iounmap:
- iounmap(hcd->regs);
-err_put_hcd:
- usb_put_hcd(hcd);
-err_dev_disable:
- ssb_device_disable(dev, flags);
- return err;
-}
-
-static int ssb_ohci_probe(struct ssb_device *dev,
- const struct ssb_device_id *id)
-{
- int err;
- u16 chipid_top;
-
- /* USBcores are only connected on embedded devices. */
- chipid_top = (dev->bus->chip_id & 0xFF00);
- if (chipid_top != 0x4700 && chipid_top != 0x5300)
- return -ENODEV;
-
- /* TODO: Probably need checks here; is the core connected? */
-
- if (usb_disabled())
- return -ENODEV;
-
- /* We currently always attach SSB_DEV_USB11_HOSTDEV
- * as HOST OHCI. If we want to attach it as Client device,
- * we must branch here and call into the (yet to
- * be written) Client mode driver. Same for remove(). */
-
- err = ssb_ohci_attach(dev);
-
- return err;
-}
-
-static void ssb_ohci_remove(struct ssb_device *dev)
-{
- ssb_ohci_detach(dev);
-}
-
-#ifdef CONFIG_PM
-
-static int ssb_ohci_suspend(struct ssb_device *dev, pm_message_t state)
-{
- ssb_device_disable(dev, 0);
-
- return 0;
-}
-
-static int ssb_ohci_resume(struct ssb_device *dev)
-{
- struct usb_hcd *hcd = ssb_get_drvdata(dev);
- struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
-
- ssb_device_enable(dev, ohcidev->enable_flags);
-
- ohci_finish_controller_resume(hcd);
- return 0;
-}
-
-#else /* !CONFIG_PM */
-#define ssb_ohci_suspend NULL
-#define ssb_ohci_resume NULL
-#endif /* CONFIG_PM */
-
-static const struct ssb_device_id ssb_ohci_table[] = {
- SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV),
- SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV),
- SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV),
- SSB_DEVTABLE_END
-};
-MODULE_DEVICE_TABLE(ssb, ssb_ohci_table);
-
-static struct ssb_driver ssb_ohci_driver = {
- .name = KBUILD_MODNAME,
- .id_table = ssb_ohci_table,
- .probe = ssb_ohci_probe,
- .remove = ssb_ohci_remove,
- .suspend = ssb_ohci_suspend,
- .resume = ssb_ohci_resume,
-};

View File

@ -0,0 +1,69 @@
From 9be402f069cc259ad5795b77567d66c4e7f6bef6 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sun, 18 Jul 2010 14:59:24 +0200
Subject: [PATCH 4/6] MIPS: BCM47xx: Setup and register serial early
Swap the first and second serial if console=ttyS1 was set.
Set it up and register it for early serial support.
This patch has been in OpenWRT for a long time.
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
arch/mips/bcm47xx/setup.c | 39 ++++++++++++++++++++++++++++++++++++++-
1 files changed, 38 insertions(+), 1 deletions(-)
--- a/arch/mips/bcm47xx/setup.c
+++ b/arch/mips/bcm47xx/setup.c
@@ -32,6 +32,8 @@
#include <linux/ssb/ssb_embedded.h>
#include <linux/bcma/bcma_soc.h>
#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <linux/serial_8250.h>
#include <asm/bootinfo.h>
#include <asm/reboot.h>
#include <asm/time.h>
@@ -279,6 +281,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;
@@ -311,6 +338,10 @@ static void __init bcm47xx_register_ssb(
memcpy(&mcore->serial_ports[1], &port, sizeof(port));
}
}
+
+#ifdef CONFIG_SERIAL_8250
+ bcm47xx_early_serial_setup(mcore);
+#endif
}
#endif

View File

@ -0,0 +1,141 @@
From 5219981646071abb6731634bf47781a53e248764 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sun, 18 Jul 2010 15:11:26 +0200
Subject: [PATCH 6/6] MIPS: BCM47xx: Remove CFE console
Do not use the CFE console. It causes hangs on some devices like the
Buffalo WHR-HP-G54.
This was reported in https://dev.openwrt.org/ticket/4061 and
https://forum.openwrt.org/viewtopic.php?id=17063
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
arch/mips/Kconfig | 1 -
arch/mips/bcm47xx/prom.c | 82 +++------------------------------------------
2 files changed, 6 insertions(+), 77 deletions(-)
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -97,7 +97,6 @@ config BCM47XX
select SYS_SUPPORTS_32BIT_KERNEL
select SYS_SUPPORTS_LITTLE_ENDIAN
select GENERIC_GPIO
- select SYS_HAS_EARLY_PRINTK
select CFE
help
Support for BCM47XX based boards
--- a/arch/mips/bcm47xx/prom.c
+++ b/arch/mips/bcm47xx/prom.c
@@ -31,96 +31,28 @@
#include <asm/fw/cfe/cfe_api.h>
#include <asm/fw/cfe/cfe_error.h>
-static int cfe_cons_handle;
-
const char *get_system_type(void)
{
return "Broadcom BCM47XX";
}
-void prom_putchar(char c)
-{
- while (cfe_write(cfe_cons_handle, &c, 1) == 0)
- ;
-}
-
-static __init void prom_init_cfe(void)
+static __init int prom_init_cfe(void)
{
uint32_t cfe_ept;
uint32_t cfe_handle;
uint32_t cfe_eptseal;
- int argc = fw_arg0;
- char **envp = (char **) fw_arg2;
- int *prom_vec = (int *) fw_arg3;
-
- /*
- * Check if a loader was used; if NOT, the 4 arguments are
- * what CFE gives us (handle, 0, EPT and EPTSEAL)
- */
- if (argc < 0) {
- cfe_handle = (uint32_t)argc;
- cfe_ept = (uint32_t)envp;
- cfe_eptseal = (uint32_t)prom_vec;
- } else {
- if ((int)prom_vec < 0) {
- /*
- * Old loader; all it gives us is the handle,
- * so use the "known" entrypoint and assume
- * the seal.
- */
- cfe_handle = (uint32_t)prom_vec;
- cfe_ept = 0xBFC00500;
- cfe_eptseal = CFE_EPTSEAL;
- } else {
- /*
- * Newer loaders bundle the handle/ept/eptseal
- * Note: prom_vec is in the loader's useg
- * which is still alive in the TLB.
- */
- cfe_handle = prom_vec[0];
- cfe_ept = prom_vec[2];
- cfe_eptseal = prom_vec[3];
- }
- }
+
+ cfe_eptseal = (uint32_t) fw_arg3;
+ cfe_handle = (uint32_t) fw_arg0;
+ cfe_ept = (uint32_t) fw_arg2;
if (cfe_eptseal != CFE_EPTSEAL) {
- /* too early for panic to do any good */
printk(KERN_ERR "CFE's entrypoint seal doesn't match.");
- while (1) ;
+ return -1;
}
cfe_init(cfe_handle, cfe_ept);
-}
-
-static __init void prom_init_console(void)
-{
- /* Initialize CFE console */
- cfe_cons_handle = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE);
-}
-
-static __init void prom_init_cmdline(void)
-{
- static char buf[COMMAND_LINE_SIZE] __initdata;
-
- /* Get the kernel command line from CFE */
- if (cfe_getenv("LINUX_CMDLINE", buf, COMMAND_LINE_SIZE) >= 0) {
- buf[COMMAND_LINE_SIZE - 1] = 0;
- strcpy(arcs_cmdline, buf);
- }
-
- /* Force a console handover by adding a console= argument if needed,
- * as CFE is not available anymore later in the boot process. */
- if ((strstr(arcs_cmdline, "console=")) == NULL) {
- /* Try to read the default serial port used by CFE */
- if ((cfe_getenv("BOOT_CONSOLE", buf, COMMAND_LINE_SIZE) < 0)
- || (strncmp("uart", buf, 4)))
- /* Default to uart0 */
- strcpy(buf, "uart0");
-
- /* Compute the new command line */
- snprintf(arcs_cmdline, COMMAND_LINE_SIZE, "%s console=ttyS%c,115200",
- arcs_cmdline, buf[4]);
- }
+ return 0;
}
static __init void prom_init_mem(void)
@@ -161,8 +93,6 @@ static __init void prom_init_mem(void)
void __init prom_init(void)
{
prom_init_cfe();
- prom_init_console();
- prom_init_cmdline();
prom_init_mem();
}

View File

@ -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
@@ -89,6 +89,7 @@ config ATH79
config BCM47XX
bool "Broadcom BCM47XX based boards"
+ select BOOT_RAW
select CEVT_R4K
select CSRC_R4K
select DMA_NONCOHERENT

View File

@ -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 */

View File

@ -0,0 +1,367 @@
--- a/arch/mips/include/asm/r4kcache.h
+++ b/arch/mips/include/asm/r4kcache.h
@@ -17,6 +17,20 @@
#include <asm/cpu-features.h>
#include <asm/mipsmtregs.h>
+#ifdef CONFIG_BCM47XX
+#include <asm/paccess.h>
+#include <linux/ssb/ssb.h>
+#define BCM4710_DUMMY_RREG() ((void) *((u8 *) KSEG1ADDR(SSB_ENUM_BASE)))
+
+#define BCM4710_FILL_TLB(addr) (*(volatile unsigned long *)(addr))
+#define BCM4710_PROTECTED_FILL_TLB(addr) ({ unsigned long x; get_dbe(x, (volatile unsigned long *)(addr)); })
+#else
+#define BCM4710_DUMMY_RREG()
+
+#define BCM4710_FILL_TLB(addr)
+#define BCM4710_PROTECTED_FILL_TLB(addr)
+#endif
+
/*
* This macro return a properly sign-extended address suitable as base address
* for indexed cache operations. Two issues here:
@@ -150,6 +164,7 @@ static inline void flush_icache_line_ind
static inline void flush_dcache_line_indexed(unsigned long addr)
{
__dflush_prologue
+ BCM4710_DUMMY_RREG();
cache_op(Index_Writeback_Inv_D, addr);
__dflush_epilogue
}
@@ -169,6 +184,7 @@ static inline void flush_icache_line(uns
static inline void flush_dcache_line(unsigned long addr)
{
__dflush_prologue
+ BCM4710_DUMMY_RREG();
cache_op(Hit_Writeback_Inv_D, addr);
__dflush_epilogue
}
@@ -176,6 +192,7 @@ static inline void flush_dcache_line(uns
static inline void invalidate_dcache_line(unsigned long addr)
{
__dflush_prologue
+ BCM4710_DUMMY_RREG();
cache_op(Hit_Invalidate_D, addr);
__dflush_epilogue
}
@@ -208,6 +225,7 @@ static inline void flush_scache_line(uns
*/
static inline void protected_flush_icache_line(unsigned long addr)
{
+ BCM4710_DUMMY_RREG();
protected_cache_op(Hit_Invalidate_I, addr);
}
@@ -219,6 +237,7 @@ static inline void protected_flush_icach
*/
static inline void protected_writeback_dcache_line(unsigned long addr)
{
+ BCM4710_DUMMY_RREG();
protected_cache_op(Hit_Writeback_Inv_D, addr);
}
@@ -339,8 +358,52 @@ static inline void invalidate_tcache_pag
: "r" (base), \
"i" (op));
+static inline void blast_dcache(void)
+{
+ unsigned long start = KSEG0;
+ unsigned long dcache_size = current_cpu_data.dcache.waysize * current_cpu_data.dcache.ways;
+ unsigned long end = (start + dcache_size);
+
+ do {
+ BCM4710_DUMMY_RREG();
+ cache_op(Index_Writeback_Inv_D, start);
+ start += current_cpu_data.dcache.linesz;
+ } while(start < end);
+}
+
+static inline void blast_dcache_page(unsigned long page)
+{
+ unsigned long start = page;
+ unsigned long end = start + PAGE_SIZE;
+
+ BCM4710_FILL_TLB(start);
+ do {
+ BCM4710_DUMMY_RREG();
+ cache_op(Hit_Writeback_Inv_D, start);
+ start += current_cpu_data.dcache.linesz;
+ } while(start < end);
+}
+
+static inline void blast_dcache_page_indexed(unsigned long page)
+{
+ unsigned long start = page;
+ unsigned long end = start + PAGE_SIZE;
+ unsigned long ws_inc = 1UL << current_cpu_data.dcache.waybit;
+ unsigned long ws_end = current_cpu_data.dcache.ways <<
+ current_cpu_data.dcache.waybit;
+ unsigned long ws, addr;
+ for (ws = 0; ws < ws_end; ws += ws_inc) {
+ start = page + ws;
+ for (addr = start; addr < end; addr += current_cpu_data.dcache.linesz) {
+ BCM4710_DUMMY_RREG();
+ cache_op(Index_Writeback_Inv_D, addr);
+ }
+ }
+}
+
+
/* build blast_xxx, blast_xxx_page, blast_xxx_page_indexed */
-#define __BUILD_BLAST_CACHE(pfx, desc, indexop, hitop, lsize) \
+#define __BUILD_BLAST_CACHE(pfx, desc, indexop, hitop, lsize, war) \
static inline void blast_##pfx##cache##lsize(void) \
{ \
unsigned long start = INDEX_BASE; \
@@ -352,6 +415,7 @@ static inline void blast_##pfx##cache##l
\
__##pfx##flush_prologue \
\
+ war \
for (ws = 0; ws < ws_end; ws += ws_inc) \
for (addr = start; addr < end; addr += lsize * 32) \
cache##lsize##_unroll32(addr|ws, indexop); \
@@ -366,6 +430,7 @@ static inline void blast_##pfx##cache##l
\
__##pfx##flush_prologue \
\
+ war \
do { \
cache##lsize##_unroll32(start, hitop); \
start += lsize * 32; \
@@ -384,6 +449,8 @@ static inline void blast_##pfx##cache##l
current_cpu_data.desc.waybit; \
unsigned long ws, addr; \
\
+ war \
+ \
__##pfx##flush_prologue \
\
for (ws = 0; ws < ws_end; ws += ws_inc) \
@@ -393,36 +460,38 @@ static inline void blast_##pfx##cache##l
__##pfx##flush_epilogue \
}
-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 16)
-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 16)
-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 16)
-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 32)
-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 32)
-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 32)
-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 64)
-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64)
-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 64)
-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 128)
-
-__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 16)
-__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 32)
-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 16)
-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 32)
-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 64)
-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 128)
+__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 16, )
+__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 16, BCM4710_FILL_TLB(start);)
+__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 16, )
+__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 32, )
+__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 32, BCM4710_FILL_TLB(start);)
+__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 32, )
+__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 64, )
+__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64, BCM4710_FILL_TLB(start);)
+__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 64, )
+__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 128, )
+
+__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 16, )
+__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 32, )
+__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 16, )
+__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 32, )
+__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 64, )
+__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 128, )
/* build blast_xxx_range, protected_blast_xxx_range */
-#define __BUILD_BLAST_CACHE_RANGE(pfx, desc, hitop, prot) \
+#define __BUILD_BLAST_CACHE_RANGE(pfx, desc, hitop, prot, war, war2) \
static inline void prot##blast_##pfx##cache##_range(unsigned long start, \
unsigned long end) \
{ \
unsigned long lsize = cpu_##desc##_line_size(); \
unsigned long addr = start & ~(lsize - 1); \
unsigned long aend = (end - 1) & ~(lsize - 1); \
+ war \
\
__##pfx##flush_prologue \
\
while (1) { \
+ war2 \
prot##cache_op(hitop, addr); \
if (addr == aend) \
break; \
@@ -432,13 +501,13 @@ static inline void prot##blast_##pfx##ca
__##pfx##flush_epilogue \
}
-__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, protected_)
-__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, protected_)
-__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, protected_)
-__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, )
-__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, )
+__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, protected_, BCM4710_PROTECTED_FILL_TLB(addr); BCM4710_PROTECTED_FILL_TLB(aend);, BCM4710_DUMMY_RREG();)
+__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, protected_,, )
+__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, protected_,, )
+__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D,, BCM4710_FILL_TLB(addr); BCM4710_FILL_TLB(aend);, BCM4710_DUMMY_RREG();)
+__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD,,, )
/* blast_inv_dcache_range */
-__BUILD_BLAST_CACHE_RANGE(inv_d, dcache, Hit_Invalidate_D, )
-__BUILD_BLAST_CACHE_RANGE(inv_s, scache, Hit_Invalidate_SD, )
+__BUILD_BLAST_CACHE_RANGE(inv_d, dcache, Hit_Invalidate_D,,,BCM4710_DUMMY_RREG();)
+__BUILD_BLAST_CACHE_RANGE(inv_s, scache, Hit_Invalidate_SD,,, )
#endif /* _ASM_R4KCACHE_H */
--- a/arch/mips/include/asm/stackframe.h
+++ b/arch/mips/include/asm/stackframe.h
@@ -449,6 +449,10 @@
.macro RESTORE_SP_AND_RET
LONG_L sp, PT_R29(sp)
.set mips3
+#ifdef CONFIG_BCM47XX
+ nop
+ nop
+#endif
eret
.set mips0
.endm
--- a/arch/mips/kernel/genex.S
+++ b/arch/mips/kernel/genex.S
@@ -52,6 +52,10 @@ NESTED(except_vec1_generic, 0, sp)
NESTED(except_vec3_generic, 0, sp)
.set push
.set noat
+#ifdef CONFIG_BCM47XX
+ nop
+ nop
+#endif
#if R5432_CP0_INTERRUPT_WAR
mfc0 k0, CP0_INDEX
#endif
--- a/arch/mips/mm/c-r4k.c
+++ b/arch/mips/mm/c-r4k.c
@@ -35,6 +35,9 @@
#include <asm/cacheflush.h> /* for run_uncached() */
+/* For enabling BCM4710 cache workarounds */
+int bcm4710 = 0;
+
/*
* Special Variant of smp_call_function for use by cache functions:
*
@@ -111,6 +114,9 @@ static void __cpuinit r4k_blast_dcache_p
{
unsigned long dc_lsize = cpu_dcache_line_size();
+ if (bcm4710)
+ r4k_blast_dcache_page = blast_dcache_page;
+ else
if (dc_lsize == 0)
r4k_blast_dcache_page = (void *)cache_noop;
else if (dc_lsize == 16)
@@ -127,6 +133,9 @@ static void __cpuinit r4k_blast_dcache_p
{
unsigned long dc_lsize = cpu_dcache_line_size();
+ if (bcm4710)
+ r4k_blast_dcache_page_indexed = blast_dcache_page_indexed;
+ else
if (dc_lsize == 0)
r4k_blast_dcache_page_indexed = (void *)cache_noop;
else if (dc_lsize == 16)
@@ -143,6 +152,9 @@ static void __cpuinit r4k_blast_dcache_s
{
unsigned long dc_lsize = cpu_dcache_line_size();
+ if (bcm4710)
+ r4k_blast_dcache = blast_dcache;
+ else
if (dc_lsize == 0)
r4k_blast_dcache = (void *)cache_noop;
else if (dc_lsize == 16)
@@ -683,6 +695,8 @@ static void local_r4k_flush_cache_sigtra
unsigned long addr = (unsigned long) arg;
R4600_HIT_CACHEOP_WAR_IMPL;
+ BCM4710_PROTECTED_FILL_TLB(addr);
+ BCM4710_PROTECTED_FILL_TLB(addr + 4);
if (dc_lsize)
protected_writeback_dcache_line(addr & ~(dc_lsize - 1));
if (!cpu_icache_snoops_remote_store && scache_size)
@@ -1346,6 +1360,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:
@@ -1402,6 +1427,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();
@@ -1462,5 +1496,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

View File

@ -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);

View File

@ -0,0 +1,61 @@
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -410,10 +410,41 @@ static void b44_wap54g10_workaround(stru
error:
pr_warning("PHY: cannot reset MII transceiver isolate bit\n");
}
+
+static inline int startswith (const char *source, const char *cmp)
+{
+ return !strncmp(source,cmp,strlen(cmp));
+}
+
+static inline void b44_bcm47xx_workarounds(struct b44 *bp)
+{
+ char buf[20];
+ /* Toshiba WRC-1000, Siemens SE505 v1, Askey RT-210W, RT-220W */
+ if (nvram_getenv("boardnum", buf, sizeof(buf)) > 0)
+ return;
+ if (simple_strtoul(buf, NULL, 0) == 100) {
+ bp->phy_addr = B44_PHY_ADDR_NO_PHY;
+ } else {
+ /* WL-HDD */
+ struct ssb_device *sdev = bp->sdev;
+ if (nvram_getenv("hardware_version", buf, sizeof(buf)) > 0)
+ return;
+ if (startswith(buf, "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 +453,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 +2120,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])){

View File

@ -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;

View File

@ -0,0 +1,42 @@
--- a/drivers/ssb/driver_chipcommon.c
+++ b/drivers/ssb/driver_chipcommon.c
@@ -318,6 +318,8 @@ void ssb_chipco_resume(struct ssb_chipco
void ssb_chipco_get_clockcpu(struct ssb_chipcommon *cc,
u32 *plltype, u32 *n, u32 *m)
{
+ if ((chipco_read32(cc, SSB_CHIPCO_CHIPID) & SSB_CHIPCO_IDMASK) == 0x5354)
+ return;
*n = chipco_read32(cc, SSB_CHIPCO_CLOCK_N);
*plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT);
switch (*plltype) {
@@ -341,6 +343,8 @@ void ssb_chipco_get_clockcpu(struct ssb_
void ssb_chipco_get_clockcontrol(struct ssb_chipcommon *cc,
u32 *plltype, u32 *n, u32 *m)
{
+ if ((chipco_read32(cc, SSB_CHIPCO_CHIPID) & SSB_CHIPCO_IDMASK) == 0x5354)
+ return;
*n = chipco_read32(cc, SSB_CHIPCO_CLOCK_N);
*plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT);
switch (*plltype) {
--- a/drivers/ssb/driver_mipscore.c
+++ b/drivers/ssb/driver_mipscore.c
@@ -241,6 +241,8 @@ u32 ssb_cpu_clock(struct ssb_mipscore *m
if ((pll_type == SSB_PLLTYPE_5) || (bus->chip_id == 0x5365)) {
rate = 200000000;
+ } else if (bus->chip_id == 0x5354) {
+ rate = 240000000;
} else {
rate = ssb_calc_clock_rate(pll_type, n, m);
}
--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -1105,6 +1105,8 @@ u32 ssb_clockspeed(struct ssb_bus *bus)
if (bus->chip_id == 0x5365) {
rate = 100000000;
+ } else if (bus->chip_id == 0x5354) {
+ rate = 120000000;
} else {
rate = ssb_calc_clock_rate(plltype, clkctl_n, clkctl_m);
if (plltype == SSB_PLLTYPE_3) /* 25Mhz, 2 dividers */

View File

@ -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

View File

@ -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);

View File

@ -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;
@@ -1506,3 +1506,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);

View File

@ -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 <linux/fuse.h>
#include <linux/fs.h>
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -1211,6 +1211,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)

View File

@ -0,0 +1,66 @@
--- a/arch/mips/include/asm/page.h
+++ b/arch/mips/include/asm/page.h
@@ -43,6 +43,7 @@
#ifndef __ASSEMBLY__
#include <linux/pfn.h>
+#include <asm/cpu-features.h>
#include <asm/io.h>
extern void build_clear_page(void);
@@ -78,13 +79,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)

View File

@ -0,0 +1,56 @@
--- a/arch/mips/bcm47xx/nvram.c
+++ b/arch/mips/bcm47xx/nvram.c
@@ -199,3 +199,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
@@ -469,3 +469,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);

View File

@ -0,0 +1,18 @@
--- a/arch/mips/pci/pci.c
+++ b/arch/mips/pci/pci.c
@@ -185,12 +185,10 @@ static int pcibios_enable_resources(stru
if ((idx == PCI_ROM_RESOURCE) &&
(!(r->flags & IORESOURCE_ROM_ENABLE)))
continue;
- if (!r->start && r->end) {
- printk(KERN_ERR "PCI: Device %s not available "
- "because of resource collisions\n",
+ if (!r->start && r->end)
+ printk(KERN_WARNING "PCI: Device %s resource"
+ "collisions detected. Ignoring...\n",
pci_name(dev));
- return -EINVAL;
- }
if (r->flags & IORESOURCE_IO)
cmd |= PCI_COMMAND_IO;
if (r->flags & IORESOURCE_MEM)

View File

@ -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

View File

@ -0,0 +1,325 @@
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -44,6 +44,7 @@
#include <linux/prefetch.h>
#include <linux/dma-mapping.h>
#include <linux/firmware.h>
+#include <linux/ssb/ssb_driver_gige.h>
#include <net/checksum.h>
#include <net/ip.h>
@@ -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;
@@ -2963,6 +2975,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);
@@ -3045,9 +3060,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;
}
@@ -3166,6 +3184,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))
@@ -3512,8 +3535,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);
@@ -4041,6 +4066,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))
@@ -7747,6 +7780,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,
@@ -7845,6 +7883,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) {
@@ -9220,6 +9266,11 @@ static void tg3_timer(unsigned long __op
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
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
@@ -9509,6 +9560,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
@@ -10867,6 +10923,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;
@@ -11827,7 +11888,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;
@@ -11843,7 +11904,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;
@@ -12573,6 +12634,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 <<
@@ -12839,6 +12907,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);
@@ -14263,6 +14334,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
@@ -14673,6 +14749,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;
@@ -15171,6 +15249,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";
@@ -15366,6 +15445,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
@@ -2922,6 +2922,9 @@ enum TG3_FLAGS {
TG3_FLAG_5717_PLUS,
TG3_FLAG_4K_FIFO_LIMIT,
TG3_FLAG_RESET_TASK_PENDING,
+ 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 */
@@ -3071,6 +3074,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
@@ -3108,7 +3112,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

View File

@ -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
-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 <aurelien@aurel32.net>
- */
-
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <linux/leds.h>
-#include <linux/mtd/physmap.h>
-#include <linux/ssb/ssb.h>
-#include <linux/interrupt.h>
-#include <linux/reboot.h>
-#include <linux/gpio.h>
-#include <asm/mach-bcm47xx/bcm47xx.h>
-
-/* GPIO definitions for the WGT634U */
-#define WGT634U_GPIO_LED 3
-#define WGT634U_GPIO_RESET 2
-#define WGT634U_GPIO_TP1 7
-#define WGT634U_GPIO_TP2 6
-#define WGT634U_GPIO_TP3 5
-#define WGT634U_GPIO_TP4 4
-#define WGT634U_GPIO_TP5 1
-
-static struct gpio_led wgt634u_leds[] = {
- {
- .name = "power",
- .gpio = WGT634U_GPIO_LED,
- .active_low = 1,
- .default_trigger = "heartbeat",
- },
-};
-
-static struct gpio_led_platform_data wgt634u_led_data = {
- .num_leds = ARRAY_SIZE(wgt634u_leds),
- .leds = wgt634u_leds,
-};
-
-static struct platform_device wgt634u_gpio_leds = {
- .name = "leds-gpio",
- .id = -1,
- .dev = {
- .platform_data = &wgt634u_led_data,
- }
-};
-
-
-/* 8MiB flash. The struct mtd_partition matches original Netgear WGT634U
- firmware. */
-static struct mtd_partition wgt634u_partitions[] = {
- {
- .name = "cfe",
- .offset = 0,
- .size = 0x60000, /* 384k */
- .mask_flags = MTD_WRITEABLE /* force read-only */
- },
- {
- .name = "config",
- .offset = 0x60000,
- .size = 0x20000 /* 128k */
- },
- {
- .name = "linux",
- .offset = 0x80000,
- .size = 0x140000 /* 1280k */
- },
- {
- .name = "jffs",
- .offset = 0x1c0000,
- .size = 0x620000 /* 6272k */
- },
- {
- .name = "nvram",
- .offset = 0x7e0000,
- .size = 0x20000 /* 128k */
- },
-};
-
-static struct physmap_flash_data wgt634u_flash_data = {
- .parts = wgt634u_partitions,
- .nr_parts = ARRAY_SIZE(wgt634u_partitions)
-};
-
-static struct resource wgt634u_flash_resource = {
- .flags = IORESOURCE_MEM,
-};
-
-static struct platform_device wgt634u_flash = {
- .name = "physmap-flash",
- .id = 0,
- .dev = { .platform_data = &wgt634u_flash_data, },
- .resource = &wgt634u_flash_resource,
- .num_resources = 1,
-};
-
-/* Platform devices */
-static struct platform_device *wgt634u_devices[] __initdata = {
- &wgt634u_flash,
- &wgt634u_gpio_leds,
-};
-
-static irqreturn_t gpio_interrupt(int irq, void *ignored)
-{
- int state;
-
- /* Interrupts are shared, check if the current one is
- a GPIO interrupt. */
- if (!ssb_chipco_irq_status(&bcm47xx_bus.ssb.chipco,
- SSB_CHIPCO_IRQ_GPIO))
- return IRQ_NONE;
-
- state = gpio_get_value(WGT634U_GPIO_RESET);
-
- /* Interrupt are level triggered, revert the interrupt polarity
- to clear the interrupt. */
- gpio_polarity(WGT634U_GPIO_RESET, state);
-
- if (!state) {
- printk(KERN_INFO "Reset button pressed");
- ctrl_alt_del();
- }
-
- return IRQ_HANDLED;
-}
-
-static int __init wgt634u_init(void)
-{
- /* There is no easy way to detect that we are running on a WGT634U
- * machine. Use the MAC address as an heuristic. Netgear Inc. has
- * been allocated ranges 00:09:5b:xx:xx:xx and 00:0f:b5:xx:xx:xx.
- */
- u8 *et0mac;
-
- 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);

View File

@ -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
+obj-y += gpio.o irq.o nvram.o prom.o serial.o setup.o time.o bus.o cfe_env.o
--- /dev/null
+++ b/arch/mips/bcm47xx/cfe_env.c
@@ -0,0 +1,229 @@
+/*
+ * CFE environment variable access
+ *
+ * Copyright 2001-2003, Broadcom Corporation
+ * Copyright 2006, Felix Fietkau <nbd@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#define NVRAM_SIZE (0x1ff0)
+static char _nvdata[NVRAM_SIZE];
+static char _valuestr[256];
+
+/*
+ * TLV types. These codes are used in the "type-length-value"
+ * encoding of the items stored in the NVRAM device (flash or EEPROM)
+ *
+ * The layout of the flash/nvram is as follows:
+ *
+ * <type> <length> <data ...> <type> <length> <data ...> <type_end>
+ *
+ * The type code of "ENV_TLV_TYPE_END" marks the end of the list.
+ * The "length" field marks the length of the data section, not
+ * including the type and length fields.
+ *
+ * Environment variables are stored as follows:
+ *
+ * <type_env> <length> <flags> <name> = <value>
+ *
+ * If bit 0 (low bit) is set, the length is an 8-bit value.
+ * If bit 0 (low bit) is clear, the length is a 16-bit value
+ *
+ * Bit 7 set indicates "user" TLVs. In this case, bit 0 still
+ * indicates the size of the length field.
+ *
+ * Flags are from the constants below:
+ *
+ */
+#define ENV_LENGTH_16BITS 0x00 /* for low bit */
+#define ENV_LENGTH_8BITS 0x01
+
+#define ENV_TYPE_USER 0x80
+
+#define ENV_CODE_SYS(n,l) (((n)<<1)|(l))
+#define ENV_CODE_USER(n,l) ((((n)<<1)|(l)) | ENV_TYPE_USER)
+
+/*
+ * The actual TLV types we support
+ */
+
+#define ENV_TLV_TYPE_END 0x00
+#define ENV_TLV_TYPE_ENV ENV_CODE_SYS(0,ENV_LENGTH_8BITS)
+
+/*
+ * Environment variable flags
+ */
+
+#define ENV_FLG_NORMAL 0x00 /* normal read/write */
+#define ENV_FLG_BUILTIN 0x01 /* builtin - not stored in flash */
+#define ENV_FLG_READONLY 0x02 /* read-only - cannot be changed */
+
+#define ENV_FLG_MASK 0xFF /* mask of attributes we keep */
+#define ENV_FLG_ADMIN 0x100 /* lets us internally override permissions */
+
+
+/* *********************************************************************
+ * _nvram_read(buffer,offset,length)
+ *
+ * Read data from the NVRAM device
+ *
+ * Input parameters:
+ * buffer - destination buffer
+ * offset - offset of data to read
+ * length - number of bytes to read
+ *
+ * Return value:
+ * number of bytes read, or <0 if error occured
+ ********************************************************************* */
+static int
+_nvram_read(unsigned char *nv_buf, unsigned char *buffer, int offset, int length)
+{
+ int i;
+ if (offset > NVRAM_SIZE)
+ return -1;
+
+ for ( i = 0; i < length; i++) {
+ buffer[i] = ((volatile unsigned char*)nv_buf)[offset + i];
+ }
+ return length;
+}
+
+
+static char*
+_strnchr(const char *dest,int c,size_t cnt)
+{
+ while (*dest && (cnt > 0)) {
+ if (*dest == c) return (char *) dest;
+ dest++;
+ cnt--;
+ }
+ return NULL;
+}
+
+
+
+/*
+ * Core support API: Externally visible.
+ */
+
+/*
+ * Get the value of an NVRAM variable
+ * @param name name of variable to get
+ * @return value of variable or NULL if undefined
+ */
+
+char*
+cfe_env_get(unsigned char *nv_buf, char* name)
+{
+ int size;
+ unsigned char *buffer;
+ unsigned char *ptr;
+ unsigned char *envval;
+ unsigned int reclen;
+ unsigned int rectype;
+ int offset;
+ int flg;
+
+ if (!strcmp(name, "nvram_type"))
+ return "cfe";
+
+ size = NVRAM_SIZE;
+ buffer = &_nvdata[0];
+
+ ptr = buffer;
+ offset = 0;
+
+ /* Read the record type and length */
+ if (_nvram_read(nv_buf, ptr,offset,1) != 1) {
+ goto error;
+ }
+
+ while ((*ptr != ENV_TLV_TYPE_END) && (size > 1)) {
+
+ /* Adjust pointer for TLV type */
+ rectype = *(ptr);
+ offset++;
+ size--;
+
+ /*
+ * Read the length. It can be either 1 or 2 bytes
+ * depending on the code
+ */
+ if (rectype & ENV_LENGTH_8BITS) {
+ /* Read the record type and length - 8 bits */
+ if (_nvram_read(nv_buf, ptr,offset,1) != 1) {
+ goto error;
+ }
+ reclen = *(ptr);
+ size--;
+ offset++;
+ }
+ else {
+ /* Read the record type and length - 16 bits, MSB first */
+ if (_nvram_read(nv_buf, ptr,offset,2) != 2) {
+ goto error;
+ }
+ reclen = (((unsigned int) *(ptr)) << 8) + (unsigned int) *(ptr+1);
+ size -= 2;
+ offset += 2;
+ }
+
+ if (reclen > size)
+ break; /* should not happen, bad NVRAM */
+
+ switch (rectype) {
+ case ENV_TLV_TYPE_ENV:
+ /* Read the TLV data */
+ if (_nvram_read(nv_buf, ptr,offset,reclen) != reclen)
+ goto error;
+ flg = *ptr++;
+ envval = (unsigned char *) _strnchr(ptr,'=',(reclen-1));
+ if (envval) {
+ *envval++ = '\0';
+ memcpy(_valuestr,envval,(reclen-1)-(envval-ptr));
+ _valuestr[(reclen-1)-(envval-ptr)] = '\0';
+#if 0
+ printk(KERN_INFO "NVRAM:%s=%s\n", ptr, _valuestr);
+#endif
+ if(!strcmp(ptr, name)){
+ return _valuestr;
+ }
+ if((strlen(ptr) > 1) && !strcmp(&ptr[1], name))
+ return _valuestr;
+ }
+ break;
+
+ default:
+ /* Unknown TLV type, skip it. */
+ break;
+ }
+
+ /*
+ * Advance to next TLV
+ */
+
+ size -= (int)reclen;
+ offset += reclen;
+
+ /* Read the next record type */
+ ptr = buffer;
+ if (_nvram_read(nv_buf, ptr,offset,1) != 1)
+ goto error;
+ }
+
+error:
+ return NULL;
+
+}
+
--- a/arch/mips/bcm47xx/nvram.c
+++ b/arch/mips/bcm47xx/nvram.c
@@ -23,6 +23,8 @@
#include <asm/mach-bcm47xx/bus.h>
static char nvram_buf[NVRAM_SPACE];
+static int cfe_env;
+extern char *cfe_env_get(char *nv_buf, const char *name);
/* Probe for NVRAM header */
static void early_nvram_init_pflash(void)
@@ -59,6 +61,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) {
@@ -181,6 +202,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;
@@ -210,6 +237,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;

View File

@ -0,0 +1,47 @@
commit 812bcf8f47b45a206948008144233bc47d5747ac
Author: Hauke Mehrtens <hauke@hauke-m.de>
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
@@ -2082,6 +2082,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
@@ -2097,11 +2098,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

View File

@ -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);

View File

@ -0,0 +1,138 @@
--- a/arch/mips/include/asm/r4kcache.h
+++ b/arch/mips/include/asm/r4kcache.h
@@ -20,10 +20,28 @@
#ifdef CONFIG_BCM47XX
#include <asm/paccess.h>
#include <linux/ssb/ssb.h>
-#define BCM4710_DUMMY_RREG() ((void) *((u8 *) KSEG1ADDR(SSB_ENUM_BASE)))
+#define BCM4710_DUMMY_RREG() bcm4710_dummy_rreg()
+
+static inline unsigned long bcm4710_dummy_rreg(void)
+{
+ return *(volatile unsigned long *)(KSEG1ADDR(SSB_ENUM_BASE));
+}
+
+#define BCM4710_FILL_TLB(addr) bcm4710_fill_tlb((void *)(addr))
+
+static inline unsigned long bcm4710_fill_tlb(void *addr)
+{
+ return *(unsigned long *)addr;
+}
+
+#define BCM4710_PROTECTED_FILL_TLB(addr) bcm4710_protected_fill_tlb((void *)(addr))
+
+static inline void bcm4710_protected_fill_tlb(void *addr)
+{
+ unsigned long x;
+ get_dbe(x, (unsigned long *)addr);;
+}
-#define BCM4710_FILL_TLB(addr) (*(volatile unsigned long *)(addr))
-#define BCM4710_PROTECTED_FILL_TLB(addr) ({ unsigned long x; get_dbe(x, (volatile unsigned long *)(addr)); })
#else
#define BCM4710_DUMMY_RREG()
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -914,6 +914,9 @@ build_get_pgde32(u32 **p, unsigned int t
#endif
uasm_i_addu(p, ptr, tmp, ptr);
#else
+#ifdef CONFIG_BCM47XX
+ uasm_i_nop(p);
+#endif
UASM_i_LA_mostly(p, ptr, pgdc);
#endif
uasm_i_mfc0(p, tmp, C0_BADVADDR); /* get faulting address */
@@ -1264,12 +1267,12 @@ static void __cpuinit build_r4000_tlb_re
/* No need for uasm_i_nop */
}
-#ifdef CONFIG_BCM47XX
- uasm_i_nop(&p);
-#endif
#ifdef CONFIG_64BIT
build_get_pmde64(&p, &l, &r, K0, K1); /* get pmd in K1 */
#else
+# ifdef CONFIG_BCM47XX
+ uasm_i_nop(&p);
+# endif
build_get_pgde32(&p, K0, K1); /* get pgd in K1 */
#endif
@@ -1281,6 +1284,9 @@ static void __cpuinit build_r4000_tlb_re
build_update_entries(&p, K0, K1);
build_tlb_write_entry(&p, &l, &r, tlb_random);
uasm_l_leave(&l, p);
+#ifdef CONFIG_BCM47XX
+ uasm_i_nop(&p);
+#endif
uasm_i_eret(&p); /* return from trap */
}
#ifdef CONFIG_HUGETLB_PAGE
@@ -1797,12 +1803,12 @@ build_r4000_tlbchange_handler_head(u32 *
{
struct work_registers wr = build_get_work_registers(p);
-#ifdef CONFIG_BCM47XX
- uasm_i_nop(p);
-#endif
#ifdef CONFIG_64BIT
build_get_pmde64(p, l, r, wr.r1, wr.r2); /* get pmd in ptr */
#else
+# ifdef CONFIG_BCM47XX
+ uasm_i_nop(p);
+# endif
build_get_pgde32(p, wr.r1, wr.r2); /* get pgd in ptr */
#endif
@@ -1841,6 +1847,9 @@ build_r4000_tlbchange_handler_tail(u32 *
build_tlb_write_entry(p, l, r, tlb_indexed);
uasm_l_leave(l, *p);
build_restore_work_registers(p);
+#ifdef CONFIG_BCM47XX
+ uasm_i_nop(p);
+#endif
uasm_i_eret(p); /* return from trap */
#ifdef CONFIG_64BIT
--- a/arch/mips/kernel/genex.S
+++ b/arch/mips/kernel/genex.S
@@ -22,6 +22,19 @@
#include <asm/page.h>
#include <asm/thread_info.h>
+#ifdef CONFIG_BCM47XX
+# ifdef eret
+# undef eret
+# endif
+# define eret \
+ .set push; \
+ .set noreorder; \
+ nop; \
+ nop; \
+ eret; \
+ .set pop;
+#endif
+
#define PANIC_PIC(msg) \
.set push; \
.set reorder; \
@@ -54,7 +67,6 @@ NESTED(except_vec3_generic, 0, sp)
.set noat
#ifdef CONFIG_BCM47XX
nop
- nop
#endif
#if R5432_CP0_INTERRUPT_WAR
mfc0 k0, CP0_INDEX
@@ -79,6 +91,9 @@ NESTED(except_vec3_r4000, 0, sp)
.set push
.set mips3
.set noat
+#ifdef CONFIG_BCM47XX
+ nop
+#endif
mfc0 k1, CP0_CAUSE
li k0, 31<<2
andi k1, k1, 0x7c

View File

@ -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.. */

View File

@ -0,0 +1,11 @@
--- a/drivers/ssb/driver_pcicore.c
+++ b/drivers/ssb/driver_pcicore.c
@@ -373,7 +373,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);
}

View File

@ -0,0 +1,13 @@
--- a/arch/mips/bcm47xx/setup.c
+++ b/arch/mips/bcm47xx/setup.c
@@ -278,6 +278,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;
}

View File

@ -0,0 +1,22 @@
--- a/arch/mips/bcm47xx/nvram.c
+++ b/arch/mips/bcm47xx/nvram.c
@@ -22,7 +22,8 @@
#include <asm/mach-bcm47xx/bcm47xx.h>
#include <asm/mach-bcm47xx/bus.h>
-static char nvram_buf[NVRAM_SPACE];
+char nvram_buf[NVRAM_SPACE];
+EXPORT_SYMBOL(nvram_buf);
static int cfe_env;
extern char *cfe_env_get(char *nv_buf, const char *name);
--- a/arch/mips/mm/cache.c
+++ b/arch/mips/mm/cache.c
@@ -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 */