diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 25f3b0a..868126c 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -53,9 +53,9 @@ config BCM47XX select SSB_DRIVER_MIPS select SSB_DRIVER_EXTIF select SSB_EMBEDDED + select SSB_B43_PCI_BRIDGE if PCI select SSB_PCICORE_HOSTMODE if PCI select GENERIC_GPIO - select SYS_HAS_EARLY_PRINTK select CFE help Support for BCM47XX based boards @@ -195,7 +195,6 @@ config MIPS_MALTA select I8259 select MIPS_BOARDS_GEN select MIPS_BONITO64 - select MIPS_CPU_SCACHE select PCI_GT64XXX_PCI0 select MIPS_MSC select SWAP_IO_SPACE @@ -1455,13 +1454,6 @@ config IP22_CPU_SCACHE bool select BOARD_SCACHE -# -# Support for a MIPS32 / MIPS64 style S-caches -# -config MIPS_CPU_SCACHE - bool - select BOARD_SCACHE - config R5000_CPU_SCACHE bool select BOARD_SCACHE diff --git a/arch/mips/bcm47xx/Makefile b/arch/mips/bcm47xx/Makefile index 35294b1..9e62430 100644 --- a/arch/mips/bcm47xx/Makefile +++ b/arch/mips/bcm47xx/Makefile @@ -3,4 +3,4 @@ # under Linux. # -obj-y := gpio.o irq.o prom.o serial.o setup.o time.o wgt634u.o +obj-y := cfe_env.o gpio.o irq.o nvram.o prom.o serial.o setup.o time.o diff --git a/arch/mips/bcm47xx/cfe_env.c b/arch/mips/bcm47xx/cfe_env.c new file mode 100644 index 0000000..c1d5eee --- /dev/null +++ b/arch/mips/bcm47xx/cfe_env.c @@ -0,0 +1,229 @@ +/* + * CFE environment variable access + * + * Copyright 2001-2003, Broadcom Corporation + * Copyright 2006, Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#define NVRAM_SIZE (0x1ff0) +static char _nvdata[NVRAM_SIZE]; +static char _valuestr[256]; + +/* + * TLV types. These codes are used in the "type-length-value" + * encoding of the items stored in the NVRAM device (flash or EEPROM) + * + * The layout of the flash/nvram is as follows: + * + * + * + * The type code of "ENV_TLV_TYPE_END" marks the end of the list. + * The "length" field marks the length of the data section, not + * including the type and length fields. + * + * Environment variables are stored as follows: + * + * = + * + * If bit 0 (low bit) is set, the length is an 8-bit value. + * If bit 0 (low bit) is clear, the length is a 16-bit value + * + * Bit 7 set indicates "user" TLVs. In this case, bit 0 still + * indicates the size of the length field. + * + * Flags are from the constants below: + * + */ +#define ENV_LENGTH_16BITS 0x00 /* for low bit */ +#define ENV_LENGTH_8BITS 0x01 + +#define ENV_TYPE_USER 0x80 + +#define ENV_CODE_SYS(n,l) (((n)<<1)|(l)) +#define ENV_CODE_USER(n,l) ((((n)<<1)|(l)) | ENV_TYPE_USER) + +/* + * The actual TLV types we support + */ + +#define ENV_TLV_TYPE_END 0x00 +#define ENV_TLV_TYPE_ENV ENV_CODE_SYS(0,ENV_LENGTH_8BITS) + +/* + * Environment variable flags + */ + +#define ENV_FLG_NORMAL 0x00 /* normal read/write */ +#define ENV_FLG_BUILTIN 0x01 /* builtin - not stored in flash */ +#define ENV_FLG_READONLY 0x02 /* read-only - cannot be changed */ + +#define ENV_FLG_MASK 0xFF /* mask of attributes we keep */ +#define ENV_FLG_ADMIN 0x100 /* lets us internally override permissions */ + + +/* ********************************************************************* + * _nvram_read(buffer,offset,length) + * + * Read data from the NVRAM device + * + * Input parameters: + * buffer - destination buffer + * offset - offset of data to read + * length - number of bytes to read + * + * Return value: + * number of bytes read, or <0 if error occured + ********************************************************************* */ +static int +_nvram_read(unsigned char *nv_buf, unsigned char *buffer, int offset, int length) +{ + int i; + if (offset > NVRAM_SIZE) + return -1; + + for ( i = 0; i < length; i++) { + buffer[i] = ((volatile unsigned char*)nv_buf)[offset + i]; + } + return length; +} + + +static char* +_strnchr(const char *dest,int c,size_t cnt) +{ + while (*dest && (cnt > 0)) { + if (*dest == c) return (char *) dest; + dest++; + cnt--; + } + return NULL; +} + + + +/* + * Core support API: Externally visible. + */ + +/* + * Get the value of an NVRAM variable + * @param name name of variable to get + * @return value of variable or NULL if undefined + */ + +char* +cfe_env_get(unsigned char *nv_buf, char* name) +{ + int size; + unsigned char *buffer; + unsigned char *ptr; + unsigned char *envval; + unsigned int reclen; + unsigned int rectype; + int offset; + int flg; + + if (!strcmp(name, "nvram_type")) + return "cfe"; + + size = NVRAM_SIZE; + buffer = &_nvdata[0]; + + ptr = buffer; + offset = 0; + + /* Read the record type and length */ + if (_nvram_read(nv_buf, ptr,offset,1) != 1) { + goto error; + } + + while ((*ptr != ENV_TLV_TYPE_END) && (size > 1)) { + + /* Adjust pointer for TLV type */ + rectype = *(ptr); + offset++; + size--; + + /* + * Read the length. It can be either 1 or 2 bytes + * depending on the code + */ + if (rectype & ENV_LENGTH_8BITS) { + /* Read the record type and length - 8 bits */ + if (_nvram_read(nv_buf, ptr,offset,1) != 1) { + goto error; + } + reclen = *(ptr); + size--; + offset++; + } + else { + /* Read the record type and length - 16 bits, MSB first */ + if (_nvram_read(nv_buf, ptr,offset,2) != 2) { + goto error; + } + reclen = (((unsigned int) *(ptr)) << 8) + (unsigned int) *(ptr+1); + size -= 2; + offset += 2; + } + + if (reclen > size) + break; /* should not happen, bad NVRAM */ + + switch (rectype) { + case ENV_TLV_TYPE_ENV: + /* Read the TLV data */ + if (_nvram_read(nv_buf, ptr,offset,reclen) != reclen) + goto error; + flg = *ptr++; + envval = (unsigned char *) _strnchr(ptr,'=',(reclen-1)); + if (envval) { + *envval++ = '\0'; + memcpy(_valuestr,envval,(reclen-1)-(envval-ptr)); + _valuestr[(reclen-1)-(envval-ptr)] = '\0'; +#if 0 + printk(KERN_INFO "NVRAM:%s=%s\n", ptr, _valuestr); +#endif + if(!strcmp(ptr, name)){ + return _valuestr; + } + if((strlen(ptr) > 1) && !strcmp(&ptr[1], name)) + return _valuestr; + } + break; + + default: + /* Unknown TLV type, skip it. */ + break; + } + + /* + * Advance to next TLV + */ + + size -= (int)reclen; + offset += reclen; + + /* Read the next record type */ + ptr = buffer; + if (_nvram_read(nv_buf, ptr,offset,1) != 1) + goto error; + } + +error: + return NULL; + +} + diff --git a/arch/mips/bcm47xx/include/nvram.h b/arch/mips/bcm47xx/include/nvram.h new file mode 100644 index 0000000..6bb18e8 --- /dev/null +++ b/arch/mips/bcm47xx/include/nvram.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2006 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __NVRAM_H +#define __NVRAM_H + +struct nvram_header { + u32 magic; + u32 len; + u32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */ + u32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */ + u32 config_ncdl; /* ncdl values for memc */ +}; + +struct nvram_tuple { + char *name; + char *value; + struct nvram_tuple *next; +}; + +#define NVRAM_HEADER 0x48534C46 /* 'FLSH' */ +#define NVRAM_VERSION 1 +#define NVRAM_HEADER_SIZE 20 +#define NVRAM_SPACE 0x8000 + +#define NVRAM_MAX_VALUE_LEN 255 +#define NVRAM_MAX_PARAM_LEN 64 + +char *nvram_get(const char *name); + +#endif diff --git a/arch/mips/bcm47xx/irq.c b/arch/mips/bcm47xx/irq.c index 325757a..f087cf6 100644 --- a/arch/mips/bcm47xx/irq.c +++ b/arch/mips/bcm47xx/irq.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2004 Florian Schirmer + * Copyright (C) 2008 Michael Buesch * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -23,10 +24,19 @@ */ #include +#include +#include #include #include +#include +#include + #include + +extern struct ssb_bus ssb_bcm47xx; + + void plat_irq_dispatch(void) { u32 cause; diff --git a/arch/mips/bcm47xx/nvram.c b/arch/mips/bcm47xx/nvram.c new file mode 100644 index 0000000..71a2106 --- /dev/null +++ b/arch/mips/bcm47xx/nvram.c @@ -0,0 +1,125 @@ +/* + * BCM947xx nvram variable access + * + * Copyright 2005, Broadcom Corporation + * Copyright 2006, Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/nvram.h" + +#define MB * 1048576 +extern struct ssb_bus ssb_bcm47xx; + +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 __init early_nvram_init(void) +{ + struct ssb_mipscore *mcore = &ssb_bcm47xx.mipscore; + struct nvram_header *header; + int i; + u32 base, lim, off; + u32 *src, *dst; + + base = mcore->flash_window; + lim = mcore->flash_window_size; + cfe_env = 0; + + + /* XXX: hack for supporting the CFE environment stuff on WGT634U */ + if (lim >= 8 MB) { + src = (u32 *) KSEG1ADDR(base + 8 MB - 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 = 0x20000; + while (off <= lim) { + /* Windowed flash access */ + header = (struct nvram_header *) KSEG1ADDR(base + off - NVRAM_SPACE); + if (header->magic == NVRAM_HEADER) + goto found; + off <<= 1; + } + + /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */ + header = (struct nvram_header *) KSEG1ADDR(base + 4096); + if (header->magic == NVRAM_HEADER) + goto found; + + header = (struct nvram_header *) KSEG1ADDR(base + 1024); + if (header->magic == NVRAM_HEADER) + goto found; + + return; + +found: + src = (u32 *) header; + dst = (u32 *) nvram_buf; + for (i = 0; i < sizeof(struct nvram_header); i += 4) + *dst++ = *src++; + for (; i < header->len && i < NVRAM_SPACE; i += 4) + *dst++ = le32_to_cpu(*src++); +} + +char *nvram_get(const char *name) +{ + char *var, *value, *end, *eq; + + if (!name) + return NULL; + + 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; + end[0] = end[1] = '\0'; + for (; *var; var = value + strlen(value) + 1) { + if (!(eq = strchr(var, '='))) + break; + value = eq + 1; + if ((eq - var) == strlen(name) && strncmp(var, name, (eq - var)) == 0) + return value; + } + + return NULL; +} + +EXPORT_SYMBOL(nvram_get); diff --git a/arch/mips/bcm47xx/prom.c b/arch/mips/bcm47xx/prom.c index 079e33d..75132cf 100644 --- a/arch/mips/bcm47xx/prom.c +++ b/arch/mips/bcm47xx/prom.c @@ -32,6 +32,7 @@ #include static int cfe_cons_handle; +static void (* __prom_putchar)(char c); const char *get_system_type(void) { @@ -40,65 +41,40 @@ const char *get_system_type(void) void prom_putchar(char c) { + if (__prom_putchar) + __prom_putchar(c); +} + +void prom_putchar_cfe(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) ; - } + if (cfe_eptseal != CFE_EPTSEAL) + return -1; cfe_init(cfe_handle, cfe_ept); + return 0; } -static __init void prom_init_console(void) +static __init void prom_init_console_cfe(void) { /* Initialize CFE console */ cfe_cons_handle = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE); } -static __init void prom_init_cmdline(void) +static __init void prom_init_cmdline_cfe(void) { char buf[CL_SIZE]; @@ -146,9 +122,12 @@ static __init void prom_init_mem(void) void __init prom_init(void) { - prom_init_cfe(); - prom_init_console(); - prom_init_cmdline(); + if (prom_init_cfe() == 0) { + //prom_init_console_cfe(); + //prom_init_cmdline_cfe(); + __prom_putchar = prom_putchar_cfe; + } + prom_init_mem(); } diff --git a/arch/mips/bcm47xx/setup.c b/arch/mips/bcm47xx/setup.c index 2f580fa..0a7bd49 100644 --- a/arch/mips/bcm47xx/setup.c +++ b/arch/mips/bcm47xx/setup.c @@ -2,7 +2,7 @@ * Copyright (C) 2004 Florian Schirmer * Copyright (C) 2005 Waldemar Brodkorb * Copyright (C) 2006 Felix Fietkau - * Copyright (C) 2006 Michael Buesch + * Copyright (C) 2006-2008 Michael Buesch * * 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 @@ -25,18 +25,28 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include #include #include +#include +#include +#include +#include +#include #include #include #include -#include #include +#include + +#include "include/nvram.h" struct ssb_bus ssb_bcm47xx; EXPORT_SYMBOL(ssb_bcm47xx); +extern void bcm47xx_pci_init(void); + static void bcm47xx_machine_restart(char *command) { printk(KERN_ALERT "Please stand by while rebooting the system...\n"); @@ -56,7 +66,7 @@ static void bcm47xx_machine_halt(void) cpu_relax(); } -static void str2eaddr(char *str, char *dest) +static void e_aton(char *str, char *dest) { int i = 0; @@ -73,52 +83,142 @@ static void str2eaddr(char *str, char *dest) } } -static int bcm47xx_get_invariants(struct ssb_bus *bus, - struct ssb_init_invariants *iv) +static void bcm47xx_fill_sprom(struct ssb_sprom *sprom) +{ + char *s; + + memset(sprom, 0xFF, sizeof(struct ssb_sprom)); + + sprom->revision = 1; + if ((s = nvram_get("il0macaddr"))) + e_aton(s, sprom->il0mac); + if ((s = nvram_get("et0macaddr"))) + e_aton(s, sprom->et0mac); + if ((s = nvram_get("et1macaddr"))) + e_aton(s, sprom->et1mac); + if ((s = nvram_get("et0phyaddr"))) + sprom->et0phyaddr = simple_strtoul(s, NULL, 0); + if ((s = nvram_get("et1phyaddr"))) + sprom->et1phyaddr = simple_strtoul(s, NULL, 0); + if ((s = nvram_get("et0mdcport"))) + sprom->et0mdcport = !!simple_strtoul(s, NULL, 10); + if ((s = nvram_get("et1mdcport"))) + sprom->et1mdcport = !!simple_strtoul(s, NULL, 10); + if ((s = nvram_get("pa0b0"))) + sprom->pa0b0 = simple_strtoul(s, NULL, 0); + if ((s = nvram_get("pa0b1"))) + sprom->pa0b1 = simple_strtoul(s, NULL, 0); + if ((s = nvram_get("pa0b2"))) + sprom->pa0b2 = simple_strtoul(s, NULL, 0); + if ((s = nvram_get("pa1b0"))) + sprom->pa1b0 = simple_strtoul(s, NULL, 0); + if ((s = nvram_get("pa1b1"))) + sprom->pa1b1 = simple_strtoul(s, NULL, 0); + if ((s = nvram_get("pa1b2"))) + sprom->pa1b2 = simple_strtoul(s, NULL, 0); + if ((s = nvram_get("wl0gpio0"))) + sprom->gpio0 = simple_strtoul(s, NULL, 0); + if ((s = nvram_get("wl0gpio1"))) + sprom->gpio1 = simple_strtoul(s, NULL, 0); + if ((s = nvram_get("wl0gpio2"))) + sprom->gpio2 = simple_strtoul(s, NULL, 0); + if ((s = nvram_get("wl0gpio3"))) + sprom->gpio3 = simple_strtoul(s, NULL, 0); + if ((s = nvram_get("pa0maxpwr"))) + sprom->maxpwr_bg = simple_strtoul(s, NULL, 0); + if ((s = nvram_get("pa1maxpwr"))) + sprom->maxpwr_a = simple_strtoul(s, NULL, 0); + if ((s = nvram_get("pa0itssit"))) + sprom->itssi_bg = simple_strtoul(s, NULL, 0); + if ((s = nvram_get("pa1itssit"))) + sprom->itssi_a = simple_strtoul(s, NULL, 0); + sprom->boardflags_lo = 0; + if ((s = nvram_get("boardflags"))) + sprom->boardflags_lo = simple_strtoul(s, NULL, 0); + sprom->boardflags_hi = 0; + if ((s = nvram_get("boardflags2"))) + sprom->boardflags_hi = simple_strtoul(s, NULL, 0); +} + +static int bcm47xx_get_invariants(struct ssb_bus *bus, struct ssb_init_invariants *iv) { - char buf[100]; - - /* Fill boardinfo structure */ - memset(&(iv->boardinfo), 0 , sizeof(struct ssb_boardinfo)); - - if (cfe_getenv("boardvendor", buf, sizeof(buf)) >= 0) - iv->boardinfo.type = (u16)simple_strtoul(buf, NULL, 0); - if (cfe_getenv("boardtype", buf, sizeof(buf)) >= 0) - iv->boardinfo.type = (u16)simple_strtoul(buf, NULL, 0); - if (cfe_getenv("boardrev", buf, sizeof(buf)) >= 0) - iv->boardinfo.rev = (u16)simple_strtoul(buf, NULL, 0); - - /* Fill sprom structure */ - memset(&(iv->sprom), 0, sizeof(struct ssb_sprom)); - iv->sprom.revision = 3; - - if (cfe_getenv("et0macaddr", buf, sizeof(buf)) >= 0) - str2eaddr(buf, iv->sprom.et0mac); - if (cfe_getenv("et1macaddr", buf, sizeof(buf)) >= 0) - str2eaddr(buf, iv->sprom.et1mac); - if (cfe_getenv("et0phyaddr", buf, sizeof(buf)) >= 0) - iv->sprom.et0phyaddr = simple_strtoul(buf, NULL, 10); - if (cfe_getenv("et1phyaddr", buf, sizeof(buf)) >= 0) - iv->sprom.et1phyaddr = simple_strtoul(buf, NULL, 10); - if (cfe_getenv("et0mdcport", buf, sizeof(buf)) >= 0) - iv->sprom.et0mdcport = simple_strtoul(buf, NULL, 10); - if (cfe_getenv("et1mdcport", buf, sizeof(buf)) >= 0) - iv->sprom.et1mdcport = simple_strtoul(buf, NULL, 10); + char *s; + + iv->boardinfo.vendor = SSB_BOARDVENDOR_BCM; + if ((s = nvram_get("boardtype"))) + iv->boardinfo.type = (u16)simple_strtoul(s, NULL, 0); + if ((s = nvram_get("boardrev"))) + iv->boardinfo.rev = (u16)simple_strtoul(s, NULL, 0); + + bcm47xx_fill_sprom(&iv->sprom); + + if ((s = nvram_get("cardbus"))) + iv->has_cardbus_slot = !!simple_strtoul(s, NULL, 10); return 0; } void __init plat_mem_setup(void) { - int err; + int i, err; + char *s; + struct ssb_mipscore *mcore; + + err = ssb_bus_ssbbus_register(&ssb_bcm47xx, SSB_ENUM_BASE, bcm47xx_get_invariants); + if (err) { + const char *msg = "Failed to initialize SSB bus (err %d)\n"; + printk(msg, err); /* Make sure the message gets out of the box. */ + panic(msg, err); + } + mcore = &ssb_bcm47xx.mipscore; - err = ssb_bus_ssbbus_register(&ssb_bcm47xx, SSB_ENUM_BASE, - bcm47xx_get_invariants); - if (err) - panic("Failed to initialize SSB bus (err %d)\n", err); + s = nvram_get("kernel_args"); + if (s && !strncmp(s, "console=ttyS1", 13)) { + struct ssb_serial_port port; + + printk("Swapping serial ports!\n"); + /* swap serial ports */ + memcpy(&port, &mcore->serial_ports[0], sizeof(port)); + memcpy(&mcore->serial_ports[0], &mcore->serial_ports[1], sizeof(port)); + memcpy(&mcore->serial_ports[1], &port, sizeof(port)); + } + + 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("Serial init done.\n"); _machine_restart = bcm47xx_machine_restart; _machine_halt = bcm47xx_machine_halt; pm_power_off = bcm47xx_machine_halt; } +static int __init bcm47xx_register_gpiodev(void) +{ + static struct resource res = { + .start = 0xFFFFFFFF, + }; + struct platform_device *pdev; + + pdev = platform_device_register_simple("GPIODEV", 0, &res, 1); + if (!pdev) { + printk(KERN_ERR "bcm47xx: GPIODEV init failed\n"); + return -ENODEV; + } + + return 0; +} +device_initcall(bcm47xx_register_gpiodev); diff --git a/arch/mips/bcm47xx/time.c b/arch/mips/bcm47xx/time.c index 0c6f47b..08c23ad 100644 --- a/arch/mips/bcm47xx/time.c +++ b/arch/mips/bcm47xx/time.c @@ -22,11 +22,17 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ - #include +#include +#include +#include +#include #include +#include +#include #include -#include + +extern struct ssb_bus ssb_bcm47xx; void __init plat_time_init(void) { diff --git a/arch/mips/bcm47xx/wgt634u.c b/arch/mips/bcm47xx/wgt634u.c deleted file mode 100644 index ef00e7f..0000000 --- a/arch/mips/bcm47xx/wgt634u.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2007 Aurelien Jarno - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* GPIO definitions for the WGT634U */ -#define WGT634U_GPIO_LED 3 -#define WGT634U_GPIO_RESET 2 -#define WGT634U_GPIO_TP1 7 -#define WGT634U_GPIO_TP2 6 -#define WGT634U_GPIO_TP3 5 -#define WGT634U_GPIO_TP4 4 -#define WGT634U_GPIO_TP5 1 - -static struct gpio_led wgt634u_leds[] = { - { - .name = "power", - .gpio = WGT634U_GPIO_LED, - .active_low = 1, - .default_trigger = "heartbeat", - }, -}; - -static struct gpio_led_platform_data wgt634u_led_data = { - .num_leds = ARRAY_SIZE(wgt634u_leds), - .leds = wgt634u_leds, -}; - -static struct platform_device wgt634u_gpio_leds = { - .name = "leds-gpio", - .id = -1, - .dev = { - .platform_data = &wgt634u_led_data, - } -}; - - -/* 8MiB flash. The struct mtd_partition matches original Netgear WGT634U - firmware. */ -static struct mtd_partition wgt634u_partitions[] = { - { - .name = "cfe", - .offset = 0, - .size = 0x60000, /* 384k */ - .mask_flags = MTD_WRITEABLE /* force read-only */ - }, - { - .name = "config", - .offset = 0x60000, - .size = 0x20000 /* 128k */ - }, - { - .name = "linux", - .offset = 0x80000, - .size = 0x140000 /* 1280k */ - }, - { - .name = "jffs", - .offset = 0x1c0000, - .size = 0x620000 /* 6272k */ - }, - { - .name = "nvram", - .offset = 0x7e0000, - .size = 0x20000 /* 128k */ - }, -}; - -static struct physmap_flash_data wgt634u_flash_data = { - .parts = wgt634u_partitions, - .nr_parts = ARRAY_SIZE(wgt634u_partitions) -}; - -static struct resource wgt634u_flash_resource = { - .flags = IORESOURCE_MEM, -}; - -static struct platform_device wgt634u_flash = { - .name = "physmap-flash", - .id = 0, - .dev = { .platform_data = &wgt634u_flash_data, }, - .resource = &wgt634u_flash_resource, - .num_resources = 1, -}; - -/* Platform devices */ -static struct platform_device *wgt634u_devices[] __initdata = { - &wgt634u_flash, - &wgt634u_gpio_leds, -}; - -static irqreturn_t gpio_interrupt(int irq, void *ignored) -{ - int state; - - /* Interrupts are shared, check if the current one is - a GPIO interrupt. */ - if (!ssb_chipco_irq_status(&ssb_bcm47xx.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 = ssb_bcm47xx.sprom.et0mac; - - if (et0mac[0] == 0x00 && - ((et0mac[1] == 0x09 && et0mac[2] == 0x5b) || - (et0mac[1] == 0x0f && et0mac[2] == 0xb5))) { - struct ssb_mipscore *mcore = &ssb_bcm47xx.mipscore; - - printk(KERN_INFO "WGT634U machine detected.\n"); - - if (!request_irq(gpio_to_irq(WGT634U_GPIO_RESET), - gpio_interrupt, IRQF_SHARED, - "WGT634U GPIO", &ssb_bcm47xx.chipco)) { - gpio_direction_input(WGT634U_GPIO_RESET); - gpio_intmask(WGT634U_GPIO_RESET, 1); - ssb_chipco_irq_mask(&ssb_bcm47xx.chipco, - SSB_CHIPCO_IRQ_GPIO, - SSB_CHIPCO_IRQ_GPIO); - } - - wgt634u_flash_data.width = mcore->flash_buswidth; - wgt634u_flash_resource.start = mcore->flash_window; - wgt634u_flash_resource.end = mcore->flash_window - + mcore->flash_window_size - - 1; - return platform_add_devices(wgt634u_devices, - ARRAY_SIZE(wgt634u_devices)); - } else - return -ENODEV; -} - -module_init(wgt634u_init); - diff --git a/arch/mips/include/asm/bootinfo.h b/arch/mips/include/asm/bootinfo.h index 610fe3a..35d7d8f 100644 --- a/arch/mips/include/asm/bootinfo.h +++ b/arch/mips/include/asm/bootinfo.h @@ -57,6 +57,12 @@ #define MACH_MIKROTIK_RB532 0 /* Mikrotik RouterBoard 532 */ #define MACH_MIKROTIK_RB532A 1 /* Mikrotik RouterBoard 532A */ +/* + * Valid machtype for group Broadcom + */ +#define MACH_GROUP_BRCM 23 /* Broadcom */ +#define MACH_BCM47XX 1 /* Broadcom BCM47xx */ + #define CL_SIZE COMMAND_LINE_SIZE extern char *system_type; diff --git a/arch/mips/include/asm/cacheflush.h b/arch/mips/include/asm/cacheflush.h index 03b1d69..517bb16 100644 --- a/arch/mips/include/asm/cacheflush.h +++ b/arch/mips/include/asm/cacheflush.h @@ -32,7 +32,7 @@ extern void (*flush_cache_all)(void); extern void (*__flush_cache_all)(void); extern void (*flush_cache_mm)(struct mm_struct *mm); -#define flush_cache_dup_mm(mm) do { (void) (mm); } while (0) +#define flush_cache_dup_mm(mm) flush_cache_mm(mm) extern void (*flush_cache_range)(struct vm_area_struct *vma, unsigned long start, unsigned long end); extern void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page, unsigned long pfn); diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h index c0047f8..e7591aa 100644 --- a/arch/mips/include/asm/cpu-features.h +++ b/arch/mips/include/asm/cpu-features.h @@ -104,6 +104,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 diff --git a/arch/mips/include/asm/mach-bcm47xx/cpu-feature-overrides.h b/arch/mips/include/asm/mach-bcm47xx/cpu-feature-overrides.h new file mode 100644 index 0000000..b4fe0c3 --- /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 */ diff --git a/arch/mips/include/asm/mach-bcm47xx/kernel-entry-init.h b/arch/mips/include/asm/mach-bcm47xx/kernel-entry-init.h new file mode 100644 index 0000000..7df0dc2 --- /dev/null +++ b/arch/mips/include/asm/mach-bcm47xx/kernel-entry-init.h @@ -0,0 +1,26 @@ +/* + * 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 Embedded Alley Solutions, Inc + * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org) + * Copyright (C) 2006 Michael Buesch + */ +#ifndef __ASM_MACH_GENERIC_KERNEL_ENTRY_H +#define __ASM_MACH_GENERIC_KERNEL_ENTRY_H + +/* Intentionally empty macro, used in head.S. Override in + * arch/mips/mach-xxx/kernel-entry-init.h when necessary. + */ + .macro kernel_entry_setup + .endm + +/* + * Do SMP slave processor setup necessary before we can savely execute C code. + */ + .macro smp_slave_setup + .endm + + +#endif /* __ASM_MACH_GENERIC_KERNEL_ENTRY_H */ diff --git a/arch/mips/include/asm/page.h b/arch/mips/include/asm/page.h index 9f946e4..65ccdf2 100644 --- a/arch/mips/include/asm/page.h +++ b/arch/mips/include/asm/page.h @@ -35,6 +35,7 @@ #ifndef __ASSEMBLY__ #include +#include #include extern void build_clear_page(void); @@ -70,13 +71,16 @@ static inline void clear_user_page(void *addr, unsigned long vaddr, 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.. diff --git a/arch/mips/include/asm/r4kcache.h b/arch/mips/include/asm/r4kcache.h index 4c140db..befb69e 100644 --- a/arch/mips/include/asm/r4kcache.h +++ b/arch/mips/include/asm/r4kcache.h @@ -17,6 +17,35 @@ #include #include +#ifdef CONFIG_BCM47XX +#include +#include +#define BCM4710_DUMMY_RREG() bcm4710_dummy_rreg() + +static inline unsigned long bcm4710_dummy_rreg(void) { + return (*(volatile unsigned long *)(KSEG1ADDR(SSB_ENUM_BASE + SSB_IMSTATE))); +} + +#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);; +} + +#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 +179,7 @@ static inline void flush_icache_line_indexed(unsigned long addr) 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 +199,7 @@ static inline void flush_icache_line(unsigned long addr) 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 +207,7 @@ static inline void flush_dcache_line(unsigned long addr) static inline void invalidate_dcache_line(unsigned long addr) { __dflush_prologue + BCM4710_DUMMY_RREG(); cache_op(Hit_Invalidate_D, addr); __dflush_epilogue } @@ -208,6 +240,7 @@ static inline void flush_scache_line(unsigned long addr) */ static inline void protected_flush_icache_line(unsigned long addr) { + BCM4710_DUMMY_RREG(); protected_cache_op(Hit_Invalidate_I, addr); } @@ -219,6 +252,7 @@ static inline void protected_flush_icache_line(unsigned long addr) */ static inline void protected_writeback_dcache_line(unsigned long addr) { + BCM4710_DUMMY_RREG(); protected_cache_op(Hit_Writeback_Inv_D, addr); } @@ -339,8 +373,52 @@ static inline void invalidate_tcache_page(unsigned long addr) : "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 +430,7 @@ static inline void blast_##pfx##cache##lsize(void) \ \ __##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 +445,7 @@ static inline void blast_##pfx##cache##lsize##_page(unsigned long page) \ \ __##pfx##flush_prologue \ \ + war \ do { \ cache##lsize##_unroll32(start, hitop); \ start += lsize * 32; \ @@ -384,6 +464,8 @@ static inline void blast_##pfx##cache##lsize##_page_indexed(unsigned long page) current_cpu_data.desc.waybit; \ unsigned long ws, addr; \ \ + war \ + \ __##pfx##flush_prologue \ \ for (ws = 0; ws < ws_end; ws += ws_inc) \ @@ -393,35 +475,37 @@ static inline void blast_##pfx##cache##lsize##_page_indexed(unsigned long page) __##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(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(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; \ @@ -431,13 +515,13 @@ static inline void prot##blast_##pfx##cache##_range(unsigned long start, \ __##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 */ diff --git a/arch/mips/include/asm/stackframe.h b/arch/mips/include/asm/stackframe.h index db0fa7b..fd23784 100644 --- a/arch/mips/include/asm/stackframe.h +++ b/arch/mips/include/asm/stackframe.h @@ -426,6 +426,10 @@ .macro RESTORE_SP_AND_RET LONG_L sp, PT_R29(sp) .set mips3 +#ifdef CONFIG_BCM47XX + nop + nop +#endif eret .set mips0 .endm diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index b13b8eb..8141700 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -753,6 +753,8 @@ static inline void cpu_probe_mips(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_25KF: c->cputype = CPU_25KF; __cpu_name[cpu] = "MIPS 25Kc"; + /* Probe for L2 cache */ + c->scache.flags &= ~MIPS_CACHE_NOT_PRESENT; break; case PRID_IMP_34K: c->cputype = CPU_34K; diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S index 8882e57..e6ddba2 100644 --- a/arch/mips/kernel/genex.S +++ b/arch/mips/kernel/genex.S @@ -22,6 +22,19 @@ #include #include +#ifdef CONFIG_BCM47XX +# ifdef eret +# undef eret +# endif +# define eret \ + .set push; \ + .set noreorder; \ + nop; \ + nop; \ + eret; \ + .set pop; +#endif + #define PANIC_PIC(msg) \ .set push; \ .set reorder; \ @@ -52,6 +65,9 @@ NESTED(except_vec1_generic, 0, sp) NESTED(except_vec3_generic, 0, sp) .set push .set noat +#ifdef CONFIG_BCM47XX + nop +#endif #if R5432_CP0_INTERRUPT_WAR mfc0 k0, CP0_INDEX #endif @@ -75,6 +91,9 @@ NESTED(except_vec3_r4000, 0, sp) .set push .set mips3 .set noat +#ifdef CONFIG_BCM47XX + nop +#endif mfc0 k1, CP0_CAUSE li k0, 31<<2 andi k1, k1, 0x7c diff --git a/arch/mips/mm/Makefile b/arch/mips/mm/Makefile index d7ec955..d666c4e 100644 --- a/arch/mips/mm/Makefile +++ b/arch/mips/mm/Makefile @@ -32,6 +32,5 @@ obj-$(CONFIG_CPU_CAVIUM_OCTEON) += c-octeon.o cex-oct.o tlb-r4k.o obj-$(CONFIG_IP22_CPU_SCACHE) += sc-ip22.o obj-$(CONFIG_R5000_CPU_SCACHE) += sc-r5k.o obj-$(CONFIG_RM7000_CPU_SCACHE) += sc-rm7k.o -obj-$(CONFIG_MIPS_CPU_SCACHE) += sc-mips.o EXTRA_CFLAGS += -Werror diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c index 171951d..258f8ca 100644 --- a/arch/mips/mm/c-r4k.c +++ b/arch/mips/mm/c-r4k.c @@ -34,6 +34,9 @@ #include /* for run_uncached() */ +/* For enabling BCM4710 cache workarounds */ +int bcm4710 = 0; + /* * Special Variant of smp_call_function for use by cache functions: * @@ -104,6 +107,9 @@ static void __cpuinit r4k_blast_dcache_page_setup(void) { 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) @@ -118,6 +124,9 @@ static void __cpuinit r4k_blast_dcache_page_indexed_setup(void) { 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) @@ -132,6 +141,9 @@ static void __cpuinit r4k_blast_dcache_setup(void) { 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) @@ -348,7 +360,7 @@ static inline void local_r4k___flush_cache_all(void * args) } } -static void r4k___flush_cache_all(void) +void r4k___flush_cache_all(void) { r4k_on_each_cpu(local_r4k___flush_cache_all, NULL, 1); } @@ -482,7 +494,7 @@ static inline void local_r4k_flush_cache_page(void *args) */ 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); @@ -505,14 +517,14 @@ static inline void local_r4k_flush_cache_page(void *args) } if (vaddr) { - if (map_coherent) + if (map_coherent && cpu_use_kmap_coherent) kunmap_coherent(); else kunmap_atomic(vaddr, KM_USER0); } } -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; @@ -667,6 +679,8 @@ static void local_r4k_flush_cache_sigtramp(void * arg) 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) @@ -1135,7 +1149,6 @@ static void __init loongson2_sc_init(void) extern int r5k_sc_init(void); extern int rm7k_sc_init(void); -extern int mips_sc_init(void); static void __cpuinit setup_scache(void) { @@ -1189,29 +1202,17 @@ static void __cpuinit setup_scache(void) #endif default: - if (c->isa_level == MIPS_CPU_ISA_M32R1 || - c->isa_level == MIPS_CPU_ISA_M32R2 || - c->isa_level == MIPS_CPU_ISA_M64R1 || - c->isa_level == MIPS_CPU_ISA_M64R2) { -#ifdef CONFIG_MIPS_CPU_SCACHE - if (mips_sc_init ()) { - scache_size = c->scache.ways * c->scache.sets * c->scache.linesz; - printk("MIPS secondary cache %ldkB, %s, linesize %d bytes.\n", - scache_size >> 10, - way_string[c->scache.ways], c->scache.linesz); - } -#else - if (!(c->scache.flags & MIPS_CACHE_NOT_PRESENT)) - panic("Dunno how to handle MIPS32 / MIPS64 second level cache"); -#endif - return; - } sc_present = 0; } if (!sc_present) return; + if ((c->isa_level == MIPS_CPU_ISA_M32R1 || + c->isa_level == MIPS_CPU_ISA_M64R1) && + !(c->scache.flags & MIPS_CACHE_NOT_PRESENT)) + panic("Dunno how to handle MIPS32 / MIPS64 second level cache"); + /* compute a couple of other cache variables */ c->scache.waysize = scache_size / c->scache.ways; @@ -1298,6 +1299,17 @@ static void __cpuinit coherency_setup(void) * silly idea of putting something else there ... */ switch (current_cpu_type()) { + case CPU_BCM3302: + { + 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: @@ -1354,6 +1366,15 @@ void __cpuinit r4k_cache_init(void) break; } + /* Check if special workarounds are required */ +#ifdef CONFIG_BCM47XX + if (current_cpu_data.cputype == CPU_BCM4710 && (current_cpu_data.processor_id & 0xff) == 0) { + printk("Enabling BCM4710A0 cache workarounds.\n"); + bcm4710 = 1; + } else +#endif + bcm4710 = 0; + probe_pcache(); setup_scache(); @@ -1412,5 +1433,20 @@ 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 } + +// fuse package DCACHE BUG patch exports +void (*fuse_flush_cache_all)(void) = r4k___flush_cache_all; +void (*fuse_flush_cache_page)(struct vm_area_struct *vma, unsigned long page, + unsigned long pfn) = r4k_flush_cache_page; +EXPORT_SYMBOL(fuse_flush_cache_page); +EXPORT_SYMBOL(fuse_flush_cache_all); diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index c551129..e733f78 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -198,35 +198,11 @@ 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 && - 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) { - 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); @@ -244,7 +220,7 @@ void copy_from_user_page(struct vm_area_struct *vma, struct page *page, unsigned long vaddr, void *dst, const void *src, unsigned long len) { - if (cpu_has_dc_aliases && + if (cpu_has_dc_aliases && cpu_use_kmap_coherent && page_mapped(page) && !Page_dcache_dirty(page)) { void *vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); memcpy(dst, vfrom, len); diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index 0615b62..b0d64df 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -544,6 +544,9 @@ build_get_pgde32(u32 **p, unsigned int tmp, unsigned int ptr) #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 */ @@ -677,6 +680,9 @@ static void __cpuinit build_r4000_tlb_refill_handler(void) #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 @@ -684,6 +690,9 @@ static void __cpuinit build_r4000_tlb_refill_handler(void) 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_64BIT @@ -1084,6 +1093,9 @@ build_r4000_tlbchange_handler_head(u32 **p, struct uasm_label **l, #ifdef CONFIG_64BIT build_get_pmde64(p, l, r, pte, ptr); /* get pmd in ptr */ #else +# ifdef CONFIG_BCM47XX + uasm_i_nop(p); +# endif build_get_pgde32(p, pte, ptr); /* get pgd in ptr */ #endif @@ -1111,6 +1123,9 @@ build_r4000_tlbchange_handler_tail(u32 **p, struct uasm_label **l, build_update_entries(p, tmp, ptr); build_tlb_write_entry(p, l, r, tlb_indexed); uasm_l_leave(l, *p); +#ifdef CONFIG_BCM47XX + uasm_i_nop(p); +#endif uasm_i_eret(p); /* return from trap */ #ifdef CONFIG_64BIT diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index b0eb9e7..85c152a 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -185,12 +185,10 @@ static int pcibios_enable_resources(struct pci_dev *dev, int mask) 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) diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 82923bd..4fa8a05 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -343,6 +343,12 @@ config MTD_CFI_FLAGADM Mapping for the Flaga digital module. If you don't have one, ignore this setting. +config MTD_BCM47XX + tristate "BCM47xx flash device" + depends on MIPS && MTD_CFI && BCM47XX + help + Support for the flash chips on the BCM947xx board. + config MTD_REDWOOD tristate "CFI Flash devices mapped on IBM Redwood" depends on MTD_CFI && ( REDWOOD_4 || REDWOOD_5 || REDWOOD_6 ) diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 2dbc1be..d45138c 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_MTD_PMC_MSP_RAMROOT)+= pmcmsp-ramroot.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o +obj-$(CONFIG_MTD_BCM47XX) += bcm47xx-flash.o obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o obj-$(CONFIG_MTD_IPAQ) += ipaq-flash.o obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o diff --git a/drivers/mtd/maps/bcm47xx-flash.c b/drivers/mtd/maps/bcm47xx-flash.c new file mode 100644 index 0000000..3ceec40 --- /dev/null +++ b/drivers/mtd/maps/bcm47xx-flash.c @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2006 Felix Fietkau + * Copyright (C) 2005 Waldemar Brodkorb + * Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org) + * + * original functions for finding root filesystem from Mike Baker + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Copyright 2001-2003, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * Flash mapping for BCM947XX boards + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_MTD_PARTITIONS +#include +#endif +#include +#ifdef CONFIG_SSB +#include +#endif +#include + + +#define TRX_MAGIC 0x30524448 /* "HDR0" */ +#define TRX_VERSION 1 +#define TRX_MAX_LEN 0x3A0000 +#define TRX_NO_HEADER 1 /* Do not write TRX header */ +#define TRX_GZ_FILES 0x2 /* Contains up to TRX_MAX_OFFSET individual gzip files */ +#define TRX_MAX_OFFSET 3 + +struct trx_header { + u32 magic; /* "HDR0" */ + u32 len; /* Length of file including header */ + u32 crc32; /* 32-bit CRC from flag_version to end of file */ + u32 flag_version; /* 0:15 flags, 16:31 version */ + u32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of header */ +}; + +#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) +#define NVRAM_SPACE 0x8000 +#define WINDOW_ADDR 0x1fc00000 +#define WINDOW_SIZE 0x400000 +#define BUSWIDTH 2 + +#ifdef CONFIG_SSB +extern struct ssb_bus ssb_bcm47xx; +#endif +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, +}; + +#ifdef CONFIG_MTD_PARTITIONS + +static struct mtd_partition bcm47xx_parts[] = { + { name: "cfe", offset: 0, size: 0, mask_flags: MTD_WRITEABLE, }, + { name: "linux", offset: 0, size: 0, }, + { name: "rootfs", offset: 0, size: 0, }, + { name: "nvram", offset: 0, size: 0, }, + { name: NULL, }, +}; + +static int __init +find_cfe_size(struct mtd_info *mtd, size_t size) +{ + 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 < size; off += blocksize) { + memset(buf, 0xe5, sizeof(buf)); + + /* + * Read into buffer + */ + if (mtd->read(mtd, off, sizeof(buf), &len, buf) || + len != sizeof(buf)) + continue; + + /* found a TRX header */ + if (le32_to_cpu(trx->magic) == TRX_MAGIC) { + goto found; + } + } + + printk(KERN_NOTICE + "%s: Couldn't find bootloader size\n", + mtd->name); + return -1; + + found: + printk(KERN_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); + printk (KERN_WARNING "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, writhe data to flash. + */ + + ret = mtd->write (mtd, pos, len, &retlen, buf); + if (ret) + return ret; + if (retlen != len) + return -EIO; + return 0; +} + + + + +static int __init +find_root(struct mtd_info *mtd, size_t size, struct mtd_partition *part) +{ + struct trx_header trx, *trx2; + unsigned char buf[512], *block; + int off, blocksize; + u32 i, crc = ~0; + size_t len; + struct squashfs_super_block *sb = (struct squashfs_super_block *) buf; + + blocksize = mtd->erasesize; + if (blocksize < 0x10000) + blocksize = 0x10000; + + for (off = (128*1024); off < 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 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; + + goto found; + } + } + + printk(KERN_NOTICE + "%s: Couldn't find root filesystem\n", + mtd->name); + return -1; + + found: + 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, mtd->erasesize, &len, block) || len != mtd->erasesize) { + printk("Error accessing the first trx eraseblock\n"); + return 0; + } + + printk("Updating TRX offsets and length:\n"); + printk("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); + printk("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, &trx, sizeof(trx)); + if (mtd->unlock) + mtd->unlock(mtd, off, mtd->erasesize); + erase_write(mtd, off, mtd->erasesize, block); + if (mtd->sync) + mtd->sync(mtd); + kfree(block); + printk("Done\n"); + } + + return part->size; +} + +struct mtd_partition * __init +init_mtd_partitions(struct mtd_info *mtd, size_t size) +{ + int cfe_size; + + if ((cfe_size = find_cfe_size(mtd,size)) < 0) + return NULL; + + /* boot loader */ + bcm47xx_parts[0].offset = 0; + bcm47xx_parts[0].size = cfe_size; + + /* nvram */ + if (cfe_size != 384 * 1024) { + bcm47xx_parts[3].offset = 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); + } + + /* linux (kernel and rootfs) */ + if (cfe_size != 384 * 1024) { + bcm47xx_parts[1].offset = bcm47xx_parts[0].size; + bcm47xx_parts[1].size = bcm47xx_parts[3].offset - + bcm47xx_parts[1].offset; + } 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 = size - + bcm47xx_parts[0].size - + (2*bcm47xx_parts[3].size) - + mtd->erasesize; + } + + /* find and size rootfs */ + find_root(mtd,size,&bcm47xx_parts[2]); + bcm47xx_parts[2].size = size - bcm47xx_parts[2].offset - bcm47xx_parts[3].size; + + return bcm47xx_parts; +} +#endif + +int __init init_bcm47xx_map(void) +{ +#ifdef CONFIG_SSB + struct ssb_mipscore *mcore = &ssb_bcm47xx.mipscore; +#endif + size_t size; + int ret = 0; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; + int i; +#endif + +#ifdef CONFIG_SSB + u32 window = mcore->flash_window; + u32 window_size = mcore->flash_window_size; + + printk("flash init: 0x%08x 0x%08x\n", window, window_size); + bcm47xx_map.phys = window; + bcm47xx_map.size = window_size; + bcm47xx_map.bankwidth = mcore->flash_buswidth; + bcm47xx_map.virt = ioremap_nocache(window, window_size); +#else + printk("flash init: 0x%08x 0x%08x\n", WINDOW_ADDR, WINDOW_SIZE); + bcm47xx_map.virt = ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE); +#endif + + if (!bcm47xx_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } + + simple_map_init(&bcm47xx_map); + + if (!(bcm47xx_mtd = do_map_probe("cfi_probe", &bcm47xx_map))) { + printk("Failed to do_map_probe\n"); + iounmap((void *)bcm47xx_map.virt); + return -ENXIO; + } + + /* override copy_from routine */ + bcm47xx_map.copy_from = bcm47xx_map_copy_from; + + bcm47xx_mtd->owner = THIS_MODULE; + + size = bcm47xx_mtd->size; + + printk(KERN_NOTICE "Flash device: 0x%x at 0x%x\n", size, WINDOW_ADDR); + +#ifdef CONFIG_MTD_PARTITIONS + parts = init_mtd_partitions(bcm47xx_mtd, size); + for (i = 0; parts[i].name; i++); + ret = add_mtd_partitions(bcm47xx_mtd, parts, i); + if (ret) { + printk(KERN_ERR "Flash: add_mtd_partitions failed\n"); + goto fail; + } +#endif + return 0; + + fail: + if (bcm47xx_mtd) + map_destroy(bcm47xx_mtd); + if (bcm47xx_map.virt) + iounmap((void *)bcm47xx_map.virt); + bcm47xx_map.virt = 0; + return ret; +} + +void __exit cleanup_bcm47xx_map(void) +{ +#ifdef CONFIG_MTD_PARTITIONS + del_mtd_partitions(bcm47xx_mtd); +#endif + map_destroy(bcm47xx_mtd); + iounmap((void *)bcm47xx_map.virt); +} + +module_init(init_bcm47xx_map); +module_exit(cleanup_bcm47xx_map); diff --git a/drivers/net/b44.c b/drivers/net/b44.c index b70b81e..2508e77 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -339,7 +339,7 @@ static int b44_phy_reset(struct b44 *bp) } } - return 0; + return err; } static void __b44_set_flow_ctrl(struct b44 *bp, u32 pause_flags) @@ -815,7 +815,7 @@ static int b44_rx(struct b44 *bp, int budget) struct sk_buff *copy_skb; b44_recycle_rx(bp, cons, bp->rx_prod); - copy_skb = dev_alloc_skb(len + 2); + copy_skb = netdev_alloc_skb(bp->dev, len + 2); if (copy_skb == NULL) goto drop_it_no_recycle; @@ -2220,6 +2220,10 @@ static int __devinit b44_init_one(struct ssb_device *sdev, */ b44_chip_reset(bp, B44_CHIP_RESET_FULL); + /* do a phy reset to test if there is an active phy */ + if (b44_phy_reset(bp) < 0) + bp->phy_addr = B44_PHY_ADDR_NO_PHY; + printk(KERN_INFO "%s: Broadcom 44xx/47xx 10/100BaseT Ethernet %pM\n", dev->name, dev->dev_addr); diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 201be42..70b7c7b 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -446,8 +447,9 @@ static void _tw32_flush(struct tg3 *tp, u32 off, u32 val, u32 usec_wait) static inline void tw32_mailbox_flush(struct tg3 *tp, u32 off, u32 val) { tp->write32_mbox(tp, off, val); - if (!(tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER) && - !(tp->tg3_flags2 & TG3_FLG2_ICH_WORKAROUND)) + if ((tp->tg3_flags3 & TG3_FLG3_FLUSH_POSTED_WRITES) || + (!(tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER) && + !(tp->tg3_flags2 & TG3_FLG2_ICH_WORKAROUND))) tp->read32_mbox(tp, off); } @@ -457,7 +459,7 @@ static void tg3_write32_tx_mbox(struct tg3 *tp, u32 off, u32 val) writel(val, mbox); if (tp->tg3_flags & TG3_FLAG_TXD_MBOX_HWBUG) writel(val, mbox); - if (tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER) + if ((tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER) || (tp->tg3_flags3 & TG3_FLG3_FLUSH_POSTED_WRITES)) readl(mbox); } @@ -729,7 +731,7 @@ static void tg3_switch_clocks(struct tg3 *tp) #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; @@ -743,7 +745,7 @@ static int tg3_readphy(struct tg3 *tp, int reg, u32 *val) *val = 0x0; - frame_val = ((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); @@ -778,7 +780,12 @@ static int tg3_readphy(struct tg3 *tp, int reg, u32 *val) 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, 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; @@ -794,7 +801,7 @@ static int tg3_writephy(struct tg3 *tp, int reg, u32 val) udelay(80); } - frame_val = ((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); @@ -827,6 +834,11 @@ static int tg3_writephy(struct tg3 *tp, int reg, u32 val) return ret; } +static int tg3_writephy(struct tg3 *tp, int reg, u32 val) +{ + return __tg3_writephy(tp, PHY_ADDR, reg, val); +} + static int tg3_bmcr_reset(struct tg3 *tp) { u32 phy_control; @@ -2262,6 +2274,9 @@ static int tg3_nvram_read(struct tg3 *tp, u32 offset, u32 *val) { int ret; + if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE) + return -ENODEV; + if (!(tp->tg3_flags & TG3_FLAG_NVRAM)) return tg3_nvram_read_using_eeprom(tp, offset, val); @@ -2595,8 +2610,10 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state) tg3_frob_aux_power(tp); /* 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 & PHY_ID_MASK != 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); @@ -3088,6 +3105,14 @@ relink: tg3_phy_copper_begin(tp); + if (tp->tg3_flags3 & TG3_FLG3_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, &tmp); if (!tg3_readphy(tp, MII_BMSR, &tmp) && (tmp & BMSR_LSTATUS)) @@ -6000,6 +6025,11 @@ static int tg3_poll_fw(struct tg3 *tp) int i; u32 val; + if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE) { + /* We don't use firmware. */ + return 0; + } + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { /* Wait up to 20ms for init done. */ for (i = 0; i < 200; i++) { @@ -6256,6 +6286,14 @@ static int tg3_chip_reset(struct tg3 *tp) tw32(0x5000, 0x400); } + if (tp->tg3_flags3 & TG3_FLG3_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) { @@ -6406,9 +6444,12 @@ static int tg3_halt_cpu(struct tg3 *tp, u32 offset) return -ENODEV; } - /* Clear firmware's nvram arbitration. */ - if (tp->tg3_flags & TG3_FLAG_NVRAM) - tw32(NVRAM_SWARB, SWARB_REQ_CLR0); + if (!(tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE)) { + /* Clear firmware's nvram arbitration. */ + if (tp->tg3_flags & TG3_FLAG_NVRAM) + tw32(NVRAM_SWARB, SWARB_REQ_CLR0); + } + return 0; } @@ -6471,6 +6512,11 @@ static int tg3_load_5701_a0_firmware_fix(struct tg3 *tp) const __be32 *fw_data; int err, i; + if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE) { + /* We don't use firmware. */ + return 0; + } + fw_data = (void *)tp->fw->data; /* Firmware blob starts with version numbers, followed by @@ -6530,6 +6576,11 @@ static int tg3_load_tso_firmware(struct tg3 *tp) unsigned long cpu_base, cpu_scratch_base, cpu_scratch_size; int err, i; + if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE) { + /* We don't use firmware. */ + return 0; + } + if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) return 0; @@ -7435,6 +7486,11 @@ static void tg3_timer(unsigned long __opaque) spin_lock(&tp->lock); + if (tp->tg3_flags3 & TG3_FLG3_FLUSH_POSTED_WRITES) { + /* BCM4785: Flush posted writes from GbE to host memory. */ + tr32(HOSTCC_MODE); + } + if (!(tp->tg3_flags & TG3_FLAG_TAGGED_STATUS)) { /* All of this garbage is because when using non-tagged * IRQ status the mailbox/status_block protocol the chip @@ -9201,6 +9257,11 @@ static int tg3_test_nvram(struct tg3 *tp) __be32 *buf; int i, j, k, err = 0, size; + if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE) { + /* We don't have NVRAM. */ + return 0; + } + if (tg3_nvram_read(tp, 0, &magic) != 0) return -EIO; @@ -9994,7 +10055,7 @@ static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 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; @@ -10013,7 +10074,7 @@ static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 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; @@ -10601,6 +10662,12 @@ static void __devinit tg3_get_57780_nvram_info(struct tg3 *tp) /* Chips other than 5700/5701 use the NVRAM for fetching info. */ static void __devinit tg3_nvram_init(struct tg3 *tp) { + if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE) { + /* No NVRAM and EEPROM on the SSB Broadcom GigE core. */ + tp->tg3_flags &= ~(TG3_FLAG_NVRAM | TG3_FLAG_NVRAM_BUFFERED); + return; + } + tw32_f(GRC_EEPROM_ADDR, (EEPROM_ADDR_FSM_RESET | (EEPROM_DEFAULT_CLOCK_PERIOD << @@ -10859,6 +10926,9 @@ static int tg3_nvram_write_block(struct tg3 *tp, u32 offset, u32 len, u8 *buf) { int ret; + if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE) + return -ENODEV; + if (tp->tg3_flags & TG3_FLAG_EEPROM_WRITE_PROT) { tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl & ~GRC_LCLCTRL_GPIO_OUTPUT1); @@ -12063,7 +12133,6 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) tp->write32 = tg3_write_flush_reg32; } - if ((tp->tg3_flags & TG3_FLAG_TXD_MBOX_HWBUG) || (tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER)) { tp->write32_tx_mbox = tg3_write32_tx_mbox; @@ -12099,6 +12168,11 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701))) tp->tg3_flags |= TG3_FLAG_SRAM_USE_CONFIG; + if (tp->tg3_flags3 & TG3_FLG3_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_FLG2_IS_NIC flag must be * determined before calling tg3_set_power_state() so that @@ -12474,6 +12548,10 @@ static int __devinit tg3_get_device_address(struct tg3 *tp) } if (!is_valid_ether_addr(&dev->dev_addr[0])) { + if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE) + ssb_gige_get_macaddr(tp->pdev, &dev->dev_addr[0]); + } + if (!is_valid_ether_addr(&dev->dev_addr[0])) { #ifdef CONFIG_SPARC if (!tg3_get_default_macaddr_sparc(tp)) return 0; @@ -12965,6 +13043,7 @@ static char * __devinit tg3_phy_string(struct tg3 *tp) case PHY_ID_BCM5704: return "5704"; case PHY_ID_BCM5705: return "5705"; case PHY_ID_BCM5750: return "5750"; + case PHY_ID_BCM5750_2: return "5750-2"; case PHY_ID_BCM5752: return "5752"; case PHY_ID_BCM5714: return "5714"; case PHY_ID_BCM5780: return "5780"; @@ -13175,6 +13254,13 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, tp->msg_enable = tg3_debug; else tp->msg_enable = TG3_DEF_MSG_ENABLE; + if (pdev_is_ssb_gige_core(pdev)) { + tp->tg3_flags3 |= TG3_FLG3_IS_SSB_CORE; + if (ssb_gige_must_flush_posted_writes(pdev)) + tp->tg3_flags3 |= TG3_FLG3_FLUSH_POSTED_WRITES; + if (ssb_gige_have_roboswitch(pdev)) + tp->tg3_flags3 |= TG3_FLG3_ROBOSWITCH; + } /* The word/byte swap controls here control register access byte * swapping. DMA data byte swapping is controlled in the GRC_MODE diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index cb4c62a..5364876 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -1849,6 +1849,9 @@ #define NIC_SRAM_RGMII_STD_IBND_DISABLE 0x00000004 #define NIC_SRAM_RGMII_EXT_IBND_RX_EN 0x00000008 #define NIC_SRAM_RGMII_EXT_IBND_TX_EN 0x00000010 +#define TG3_FLG3_IS_SSB_CORE 0x00000800 +#define TG3_FLG3_FLUSH_POSTED_WRITES 0x00001000 +#define TG3_FLG3_ROBOSWITCH 0x00002000 #define NIC_SRAM_RX_MINI_BUFFER_DESC 0x00001000 @@ -2695,6 +2698,7 @@ struct tg3 { #define PHY_ID_BCM5714 0x60008340 #define PHY_ID_BCM5780 0x60008350 #define PHY_ID_BCM5755 0xbc050cc0 +#define PHY_ID_BCM5750_2 0xbc050cd0 #define PHY_ID_BCM5787 0xbc050ce0 #define PHY_ID_BCM5756 0xbc050ed0 #define PHY_ID_BCM5784 0xbc050fa0 @@ -2739,7 +2743,7 @@ struct tg3 { (X) == PHY_ID_BCM5780 || (X) == PHY_ID_BCM5787 || \ (X) == PHY_ID_BCM5755 || (X) == PHY_ID_BCM5756 || \ (X) == PHY_ID_BCM5906 || (X) == PHY_ID_BCM5761 || \ - (X) == PHY_ID_BCM8002) + (X) == PHY_ID_BCM8002 || (X) == PHY_ID_BCM5750_2) struct tg3_hw_stats *hw_stats; dma_addr_t stats_mapping; diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig index 540a294..6c2f443 100644 --- a/drivers/ssb/Kconfig +++ b/drivers/ssb/Kconfig @@ -126,6 +126,8 @@ config SSB_DRIVER_MIPS config SSB_EMBEDDED bool depends on SSB_DRIVER_MIPS + select USB_EHCI_HCD_SSB + select USB_OHCI_HCD_SSB default y config SSB_DRIVER_EXTIF diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c index 9681536..7a26028 100644 --- a/drivers/ssb/driver_chipcommon.c +++ b/drivers/ssb/driver_chipcommon.c @@ -258,6 +258,8 @@ void ssb_chipco_resume(struct ssb_chipcommon *cc) 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) { @@ -281,6 +283,8 @@ void ssb_chipco_get_clockcpu(struct ssb_chipcommon *cc, 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) { diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c index 3fd3e3b..f30186d 100644 --- a/drivers/ssb/driver_mipscore.c +++ b/drivers/ssb/driver_mipscore.c @@ -49,29 +49,54 @@ static const u32 ipsflag_irq_shift[] = { static inline u32 ssb_irqflag(struct ssb_device *dev) { - return ssb_read32(dev, SSB_TPSFLAG) & SSB_TPSFLAG_BPFLAG; + u32 tpsflag = ssb_read32(dev, SSB_TPSFLAG); + if (tpsflag) + return ssb_read32(dev, SSB_TPSFLAG) & SSB_TPSFLAG_BPFLAG; + else + /* not irq supported */ + return 0x3f; +} + +static struct ssb_device *find_device(struct ssb_device *rdev, int irqflag) +{ + struct ssb_bus *bus = rdev->bus; + int i; + for (i = 0; i < bus->nr_devices; i++) { + struct ssb_device *dev; + dev = &(bus->devices[i]); + if (ssb_irqflag(dev) == irqflag) + return dev; + } + return NULL; } /* Get the MIPS IRQ assignment for a specified device. * If unassigned, 0 is returned. + * If disabled, 5 is returned. + * If not supported, 6 is returned. */ unsigned int ssb_mips_irq(struct ssb_device *dev) { struct ssb_bus *bus = dev->bus; + struct ssb_device *mdev = bus->mipscore.dev; u32 irqflag; u32 ipsflag; u32 tmp; unsigned int irq; irqflag = ssb_irqflag(dev); + if (irqflag == 0x3f) + return 6; ipsflag = ssb_read32(bus->mipscore.dev, SSB_IPSFLAG); for (irq = 1; irq <= 4; irq++) { tmp = ((ipsflag & ipsflag_irq_mask[irq]) >> ipsflag_irq_shift[irq]); if (tmp == irqflag) break; } - if (irq == 5) - irq = 0; + if (irq == 5) { + if ((1 << irqflag) & ssb_read32(mdev, SSB_INTVEC)) + irq = 0; + } return irq; } @@ -97,25 +122,56 @@ static void set_irq(struct ssb_device *dev, unsigned int irq) struct ssb_device *mdev = bus->mipscore.dev; u32 irqflag = ssb_irqflag(dev); + BUG_ON(oldirq == 6); + dev->irq = irq + 2; - ssb_dprintk(KERN_INFO PFX - "set_irq: core 0x%04x, irq %d => %d\n", - dev->id.coreid, oldirq, irq); /* clear the old irq */ if (oldirq == 0) ssb_write32(mdev, SSB_INTVEC, (~(1 << irqflag) & ssb_read32(mdev, SSB_INTVEC))); - else + else if (oldirq != 5) clear_irq(bus, oldirq); /* assign the new one */ if (irq == 0) { ssb_write32(mdev, SSB_INTVEC, ((1 << irqflag) | ssb_read32(mdev, SSB_INTVEC))); } else { + u32 ipsflag = ssb_read32(mdev, SSB_IPSFLAG); + if ((ipsflag & ipsflag_irq_mask[irq]) != ipsflag_irq_mask[irq]) { + u32 oldipsflag = (ipsflag & ipsflag_irq_mask[irq]) >> ipsflag_irq_shift[irq]; + struct ssb_device *olddev = find_device(dev, oldipsflag); + if (olddev) + set_irq(olddev, 0); + } irqflag <<= ipsflag_irq_shift[irq]; - irqflag |= (ssb_read32(mdev, SSB_IPSFLAG) & ~ipsflag_irq_mask[irq]); + irqflag |= (ipsflag & ~ipsflag_irq_mask[irq]); ssb_write32(mdev, SSB_IPSFLAG, irqflag); } + ssb_dprintk(KERN_INFO PFX + "set_irq: core 0x%04x, irq %d => %d\n", + dev->id.coreid, oldirq+2, irq+2); +} + +static void print_irq(struct ssb_device *dev, unsigned int irq) +{ + int i; + static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"}; + ssb_dprintk(KERN_INFO PFX + "core 0x%04x, irq :", dev->id.coreid); + for (i = 0; i <= 6; i++) { + ssb_dprintk(" %s%s", irq_name[i], i==irq?"*":" "); + } + ssb_dprintk("\n"); +} + +static void dump_irq(struct ssb_bus *bus) +{ + int i; + for (i = 0; i < bus->nr_devices; i++) { + struct ssb_device *dev; + dev = &(bus->devices[i]); + print_irq(dev, ssb_mips_irq(dev)); + } } static void ssb_mips_serial_init(struct ssb_mipscore *mcore) @@ -161,6 +217,8 @@ u32 ssb_cpu_clock(struct ssb_mipscore *mcore) 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); } @@ -195,18 +253,26 @@ void ssb_mipscore_init(struct ssb_mipscore *mcore) else if (bus->chipco.dev) ssb_chipco_timing_init(&bus->chipco, ns); + dump_irq(bus); /* Assign IRQs to all cores on the bus, start with irq line 2, because serial usually takes 1 */ for (irq = 2, i = 0; i < bus->nr_devices; i++) { + int mips_irq; dev = &(bus->devices[i]); - dev->irq = ssb_mips_irq(dev) + 2; + mips_irq = ssb_mips_irq(dev); + if (mips_irq > 4) + dev->irq = 0; + else + dev->irq = mips_irq + 2; + if (dev->irq > 5) + continue; switch (dev->id.coreid) { case SSB_DEV_USB11_HOST: /* shouldn't need a separate irq line for non-4710, most of them have a proper * external usb controller on the pci */ if ((bus->chip_id == 0x4710) && (irq <= 4)) { set_irq(dev, irq++); - break; } + break; /* fallthrough */ case SSB_DEV_PCI: case SSB_DEV_ETHERNET: @@ -220,6 +286,8 @@ void ssb_mipscore_init(struct ssb_mipscore *mcore) } } } + ssb_dprintk(KERN_INFO PFX "after irq reconfiguration\n"); + dump_irq(bus); ssb_mips_serial_init(mcore); ssb_mips_flash_detect(mcore); diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index 65a1ed9..4705122 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -1010,6 +1010,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 */ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 845479f..59f8079 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -106,6 +106,19 @@ config USB_OXU210HP_HCD To compile this driver as a module, choose M here: the module will be called oxu210hp-hcd. +config USB_EHCI_HCD_SSB + bool "EHCI support for Broadcom SSB EHCI core" + depends on USB_EHCI_HCD && SSB && EXPERIMENTAL + default n + ---help--- + Support for the Sonics Silicon Backplane (SSB) attached + Broadcom USB EHCI core. + + This device is present in some embedded devices with + Broadcom based SSB bus. + + If unsure, say N. + config USB_ISP116X_HCD tristate "ISP116X HCD support" depends on USB diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index c637207..5c248d9 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1072,8 +1072,16 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ixp4xx_ehci_driver #endif -#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ - !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) +#ifdef CONFIG_USB_EHCI_HCD_SSB +#include "ehci-ssb.c" +#define SSB_EHCI_DRIVER ssb_ehci_driver +#endif + +#if !defined(PCI_DRIVER) && \ + !defined(PLATFORM_DRIVER) && \ + !defined(PS3_SYSTEM_BUS_DRIVER) && \ + !defined(OF_PLATFORM_DRIVER) && \ + !defined(SSB_EHCI_DRIVER) #error "missing bus glue for ehci-hcd" #endif diff --git a/drivers/usb/host/ehci-ssb.c b/drivers/usb/host/ehci-ssb.c new file mode 100644 index 0000000..be90a5c --- /dev/null +++ b/drivers/usb/host/ehci-ssb.c @@ -0,0 +1,201 @@ +/* + * Sonics Silicon Backplane + * Broadcom USB-core EHCI driver (SSB bus glue) + * + * Copyright 2007 Steven Brown + * + * Derived from the OHCI-SSB driver + * Copyright 2007 Michael Buesch + * + * Derived from the EHCI-PCI driver + * Copyright (c) 2000-2004 by David Brownell + * + * Derived from the OHCI-PCI driver + * Copyright 1999 Roman Weissgaerber + * Copyright 2000-2002 David Brownell + * Copyright 1999 Linus Torvalds + * Copyright 1999 Gregory P. Smith + * + * Derived from the USBcore related parts of Broadcom-SB + * Copyright 2005 Broadcom Corporation + * + * Licensed under the GNU/GPL. See COPYING for details. + */ +#include + +#define SSB_OHCI_TMSLOW_HOSTMODE (1 << 29) + +struct ssb_ehci_device { + struct ehci_hcd ehci; /* _must_ be at the beginning. */ + + u32 enable_flags; +}; + +static inline +struct ssb_ehci_device *hcd_to_ssb_ehci(struct usb_hcd *hcd) +{ + return (struct ssb_ehci_device *)(hcd->hcd_priv); +} + + +static int ssb_ehci_reset(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int err; + + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + err = ehci_halt(ehci); + + if (err) + return err; + + err = ehci_init(hcd); + + if (err) + return err; + + ehci_port_power(ehci, 0); + + return err; +} + +static int ssb_ehci_start(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int err; + + err = ehci_run(hcd); + if (err < 0) { + ehci_err(ehci, "can't start\n"); + ehci_stop(hcd); + } + + return err; +} + +#ifdef CONFIG_PM +static int ssb_ehci_hcd_suspend(struct usb_hcd *hcd, pm_message_t message) +{ + struct ssb_ehci_device *ehcidev = hcd_to_ssb_ehci(hcd); + struct ehci_hcd *ehci = &ehcidev->ehci; + unsigned long flags; + + spin_lock_irqsave(&ehci->lock, flags); + + ehci_writel(ehci, EHCI_INTR_MIE, &ehci->regs->intrdisable); + ehci_readl(ehci, &ehci->regs->intrdisable); /* commit write */ + + /* make sure snapshot being resumed re-enumerates everything */ + if (message.event == PM_EVENT_PRETHAW) + ehci_usb_reset(ehci); + + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + spin_unlock_irqrestore(&ehci->lock, flags); + return 0; +} + +static int ssb_ehci_hcd_resume(struct usb_hcd *hcd) +{ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + usb_hcd_resume_root_hub(hcd); + return 0; +} +#endif /* CONFIG_PM */ + +static const struct hc_driver ssb_ehci_hc_driver = { + .description = "ssb-usb-ehci", + .product_desc = "SSB EHCI Controller", + .hcd_priv_size = sizeof(struct ssb_ehci_device), + + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + .reset = ssb_ehci_reset, + .start = ssb_ehci_start, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + +#ifdef CONFIG_PM + .suspend = ssb_ehci_hcd_suspend, + .resume = ssb_ehci_hcd_resume, +#endif + + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + .get_frame_number = ehci_get_frame, + + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +#endif + +}; + +static void ssb_ehci_detach(struct ssb_device *dev, struct usb_hcd *hcd) +{ + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + usb_put_hcd(hcd); +} +EXPORT_SYMBOL_GPL(ssb_ehci_detach); + +static int ssb_ehci_attach(struct ssb_device *dev, struct usb_hcd **ehci_hcd) +{ + struct ssb_ehci_device *ehcidev; + struct usb_hcd *hcd; + int err = -ENOMEM; + u32 tmp, flags = 0; + + hcd = usb_create_hcd(&ssb_ehci_hc_driver, dev->dev, + dev_name(dev->dev)); + if (!hcd) + goto err_dev_disable; + + ehcidev = hcd_to_ssb_ehci(hcd); + ehcidev->enable_flags = flags; + tmp = ssb_read32(dev, SSB_ADMATCH0); + hcd->rsrc_start = ssb_admatch_base(tmp) + 0x800; /* ehci core offset */ + hcd->rsrc_len = 0x100; /* ehci reg block size */ + /* + * 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, dev->irq, IRQF_SHARED | IRQF_DISABLED); + if (err) + goto err_iounmap; + + *ehci_hcd = 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; +} +EXPORT_SYMBOL_GPL(ssb_ehci_attach); + +static const struct ssb_device_id ssb_ehci_table[] = { + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV), + SSB_DEVTABLE_END +}; +MODULE_DEVICE_TABLE(ssb, ssb_ehci_table); diff --git a/drivers/usb/host/ohci-ssb.c b/drivers/usb/host/ohci-ssb.c index 23fd6a8..599f7b5 100644 --- a/drivers/usb/host/ohci-ssb.c +++ b/drivers/usb/host/ohci-ssb.c @@ -17,6 +17,8 @@ */ #include +extern int ssb_ehci_attach(struct ssb_device *dev, struct usb_hcd **hcd); +extern void ssb_ehci_detach(struct ssb_device *dev, struct usb_hcd *hcd); #define SSB_OHCI_TMSLOW_HOSTMODE (1 << 29) @@ -24,6 +26,7 @@ struct ssb_ohci_device { struct ohci_hcd ohci; /* _must_ be at the beginning. */ u32 enable_flags; + struct usb_hcd *ehci_hcd; }; static inline @@ -92,13 +95,25 @@ static const struct hc_driver ssb_ohci_hc_driver = { static void ssb_ohci_detach(struct ssb_device *dev) { struct usb_hcd *hcd = ssb_get_drvdata(dev); +#ifdef CONFIG_USB_EHCI_HCD_SSB + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); +#endif usb_remove_hcd(hcd); iounmap(hcd->regs); usb_put_hcd(hcd); + +#ifdef CONFIG_USB_EHCI_HCD_SSB + /* + * Also detach ehci function + */ + if (dev->id.coreid == SSB_DEV_USB20_HOST) + ssb_ehci_detach(dev, ohcidev->ehci_hcd); +#endif ssb_device_disable(dev, 0); } + static int ssb_ohci_attach(struct ssb_device *dev) { struct ssb_ohci_device *ohcidev; @@ -106,10 +121,64 @@ static int ssb_ohci_attach(struct ssb_device *dev) int err = -ENOMEM; u32 tmp, flags = 0; - if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) + /* + * THE FOLLOWING COMMENTS PRESERVED FROM GPL SOURCE RELEASE + * + * The USB core requires a special bit to be set during core + * reset to enable host (OHCI) mode. Resetting the SB core in + * pcibios_enable_device() is a hack for compatibility with + * vanilla usb-ohci so that it does not have to know about + * SB. A driver that wants to use the USB core in device mode + * should know about SB and should reset the bit back to 0 + * after calling pcibios_enable_device(). + */ + + if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) { flags |= SSB_OHCI_TMSLOW_HOSTMODE; + ssb_device_enable(dev, flags); + } + + /* + * 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 SB reset sequence, the Host Control + * Register must be programmed to bring the USB core and various + * phy components out of reset. + */ + + else if (dev->id.coreid == SSB_DEV_USB20_HOST) { +#warning FIX ME need test for core being up & exit + ssb_device_enable(dev, 0); + ssb_write32(dev, 0x200, 0x7ff); + udelay(1); + if (dev->id.revision == 1) { // bug in rev 1 + + /* Change Flush control reg */ + tmp = ssb_read32(dev, 0x400); + tmp &= ~8; + ssb_write32(dev, 0x400, tmp); + tmp = ssb_read32(dev, 0x400); + printk("USB20H fcr: 0x%0x\n", tmp); + + /* Change Shim control reg */ + tmp = ssb_read32(dev, 0x304); + tmp &= ~0x100; + ssb_write32(dev, 0x304, tmp); + tmp = ssb_read32(dev, 0x304); + printk("USB20H shim: 0x%0x\n", tmp); + } + } + else + ssb_device_enable(dev, 0); - ssb_device_enable(dev, flags); + /* + * Set dma mask - 32 bit mask is just an assumption + */ + if (ssb_dma_set_mask(dev, DMA_32BIT_MASK)) + return -EOPNOTSUPP; hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev, dev_name(dev->dev)); @@ -130,6 +199,14 @@ static int ssb_ohci_attach(struct ssb_device *dev) ssb_set_drvdata(dev, hcd); +#ifdef CONFIG_USB_EHCI_HCD_SSB + /* + * attach ehci function in this core + */ + if (dev->id.coreid == SSB_DEV_USB20_HOST) + err = ssb_ehci_attach(dev, &(ohcidev->ehci_hcd)); +#endif + return err; err_iounmap: @@ -200,6 +277,7 @@ static int ssb_ohci_resume(struct ssb_device *dev) 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); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 5eb8f21..fadc751 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -764,6 +764,12 @@ config TXX9_WDT help Hardware driver for the built-in watchdog timer on TXx9 MIPS SoCs. +config BCM47XX_WDT + tristate "Broadcom BCM47xx Watchdog Timer" + depends on BCM47XX + help + Hardware driver for the Broadcom BCM47xx Watchog Timer. + # PARISC Architecture # POWERPC Architecture diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 7f8c56b..99eab0f 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -105,6 +105,7 @@ obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o obj-$(CONFIG_AR7_WDT) += ar7_wdt.o obj-$(CONFIG_TXX9_WDT) += txx9wdt.o +obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o # PARISC Architecture diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c new file mode 100644 index 0000000..3a192c7 --- /dev/null +++ b/drivers/watchdog/bcm47xx_wdt.c @@ -0,0 +1,286 @@ +/* + * Watchdog driver for Broadcom BCM47XX + * + * Copyright (C) 2008 Aleksandar Radovanovic + * Copyright (C) 2009 Matthieu CASTET + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "bcm47xx_wdt" + +#define WDT_DEFAULT_TIME 30 /* seconds */ +#define WDT_MAX_TIME 256 /* seconds */ + +static int wdt_time = WDT_DEFAULT_TIME; +static int nowayout = WATCHDOG_NOWAYOUT; + +module_param(wdt_time, int, 0); +MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default=" + __MODULE_STRING(WDT_DEFAULT_TIME) ")"); + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +#endif + +static unsigned long bcm47xx_wdt_busy; +static char expect_release; +static struct timer_list wdt_timer; +static atomic_t ticks; + +static inline void bcm47xx_wdt_hw_start(void) +{ + /* this is 2,5s on 100Mhz clock and 2s on 133 Mhz */ + ssb_watchdog_timer_set(&ssb_bcm47xx, 0xfffffff); +} + +static inline int bcm47xx_wdt_hw_stop(void) +{ + return ssb_watchdog_timer_set(&ssb_bcm47xx, 0); +} + +static void bcm47xx_timer_tick(unsigned long unused) +{ + if (!atomic_dec_and_test(&ticks)) { + bcm47xx_wdt_hw_start(); + mod_timer(&wdt_timer, jiffies + HZ); + } else { + printk(KERN_CRIT DRV_NAME "Watchdog will fire soon!!!\n"); + } +} + +static inline void bcm47xx_wdt_pet(void) +{ + atomic_set(&ticks, wdt_time); +} + +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(); +} + +static void bcm47xx_wdt_stop(void) +{ + bcm47xx_wdt_pause(); +} + +static int bcm47xx_wdt_settimeout(int new_time) +{ + if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) + return -EINVAL; + + wdt_time = new_time; + return 0; +} + +static int bcm47xx_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &bcm47xx_wdt_busy)) + return -EBUSY; + + bcm47xx_wdt_start(); + return nonseekable_open(inode, file); +} + +static int bcm47xx_wdt_release(struct inode *inode, struct file *file) +{ + if (expect_release == 42) { + bcm47xx_wdt_stop(); + } else { + printk(KERN_CRIT DRV_NAME + ": Unexpected close, not stopping watchdog!\n"); + bcm47xx_wdt_start(); + } + + clear_bit(0, &bcm47xx_wdt_busy); + expect_release = 0; + return 0; +} + +static ssize_t bcm47xx_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + if (len) { + if (!nowayout) { + size_t i; + + expect_release = 0; + + for (i = 0; i != len; i++) { + char c; + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + expect_release = 42; + } + } + bcm47xx_wdt_pet(); + } + return len; +} + +static struct watchdog_info bcm47xx_wdt_info = { + .identity = DRV_NAME, + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +static long bcm47xx_wdt_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int new_value, retval = -EINVAL;; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &bcm47xx_wdt_info, + sizeof(bcm47xx_wdt_info)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_SETOPTIONS: + if (get_user(new_value, p)) + return -EFAULT; + + if (new_value & WDIOS_DISABLECARD) { + bcm47xx_wdt_stop(); + retval = 0; + } + + if (new_value & WDIOS_ENABLECARD) { + bcm47xx_wdt_start(); + retval = 0; + } + + return retval; + + case WDIOC_KEEPALIVE: + bcm47xx_wdt_pet(); + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(new_value, p)) + return -EFAULT; + + if (bcm47xx_wdt_settimeout(new_value)) + return -EINVAL; + + bcm47xx_wdt_pet(); + + case WDIOC_GETTIMEOUT: + return put_user(wdt_time, p); + + default: + return -ENOTTY; + } +} + +static int bcm47xx_wdt_notify_sys(struct notifier_block *this, + unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + bcm47xx_wdt_stop(); + return NOTIFY_DONE; +} + +static const struct file_operations bcm47xx_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .unlocked_ioctl = bcm47xx_wdt_ioctl, + .open = bcm47xx_wdt_open, + .release = bcm47xx_wdt_release, + .write = bcm47xx_wdt_write, +}; + +static struct miscdevice bcm47xx_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &bcm47xx_wdt_fops, +}; + +static struct notifier_block bcm47xx_wdt_notifier = { + .notifier_call = bcm47xx_wdt_notify_sys, +}; + +static int __init bcm47xx_wdt_init(void) +{ + int ret; + + if (bcm47xx_wdt_hw_stop() < 0) + return -ENODEV; + + setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L); + + if (bcm47xx_wdt_settimeout(wdt_time)) { + bcm47xx_wdt_settimeout(WDT_DEFAULT_TIME); + printk(KERN_INFO DRV_NAME + ": wdt_time value must be 1 <= wdt_time <= 256, using %d\n", + wdt_time); + } + + ret = register_reboot_notifier(&bcm47xx_wdt_notifier); + if (ret) + return ret; + + ret = misc_register(&bcm47xx_wdt_miscdev); + if (ret) { + unregister_reboot_notifier(&bcm47xx_wdt_notifier); + return ret; + } + + printk(KERN_INFO "BCM47xx Watchdog Timer enabled (%d seconds%s)\n", + wdt_time, nowayout ? ", nowayout" : ""); + return 0; +} + +static void __exit bcm47xx_wdt_exit(void) +{ + if (!nowayout) + bcm47xx_wdt_stop(); + + misc_deregister(&bcm47xx_wdt_miscdev); + + unregister_reboot_notifier(&bcm47xx_wdt_notifier); +} + +module_init(bcm47xx_wdt_init); +module_exit(bcm47xx_wdt_exit); + +MODULE_AUTHOR("Aleksandar Radovanovic"); +MODULE_DESCRIPTION("Watchdog driver for Broadcom BCM47xx"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index ba76b68..c22aefd 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -527,6 +527,11 @@ static void fuse_copy_finish(struct fuse_copy_state *cs) } } +#ifdef DCACHE_BUG +extern void (*fuse_flush_cache_all)(void); +extern void (*fuse_flush_cache_page)(struct vm_area_struct *vma, unsigned long page, unsigned long pfn); +#endif + /* * Get another pagefull of userspace buffer, and map it to kernel * address space, and lock request @@ -535,6 +540,9 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) { unsigned long offset; int err; +#ifdef DCACHE_BUG + struct vm_area_struct *vma; +#endif unlock_request(cs->fc, cs->req); fuse_copy_finish(cs); @@ -546,14 +554,22 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) cs->nr_segs--; } down_read(¤t->mm->mmap_sem); +#ifndef DCACHE_BUG err = get_user_pages(current, current->mm, cs->addr, 1, cs->write, 0, &cs->pg, NULL); +#else + err = get_user_pages(current, current->mm, cs->addr, 1, cs->write, 0, + &cs->pg, &vma); +#endif up_read(¤t->mm->mmap_sem); if (err < 0) return err; BUG_ON(err != 1); offset = cs->addr % PAGE_SIZE; cs->mapaddr = kmap_atomic(cs->pg, KM_USER0); +#ifdef DCACHE_BUG + fuse_flush_cache_page(vma, cs->addr, page_to_pfn(cs->pg)); +#endif cs->buf = cs->mapaddr + offset; cs->len = min(PAGE_SIZE - offset, cs->seglen); cs->seglen -= cs->len; @@ -567,6 +583,11 @@ 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 diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 6fc5aed..ca2cc6b 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -8,6 +8,7 @@ #ifndef _FS_FUSE_I_H #define _FS_FUSE_I_H +#define DCACHE_BUG #include #include diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 91f7c85..8827ce8 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1055,6 +1055,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 enabled\n"); +#endif + INIT_LIST_HEAD(&fuse_conn_list); res = fuse_fs_init(); if (res) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 0f71812..6087a7d 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2106,6 +2106,7 @@ #define PCI_DEVICE_ID_TIGON3_5906M 0x1713 #define PCI_DEVICE_ID_BCM4401 0x4401 #define PCI_DEVICE_ID_BCM4401B0 0x4402 +#define PCI_DEVICE_ID_BCM4713 0x4713 #define PCI_VENDOR_ID_TOPIC 0x151f #define PCI_DEVICE_ID_TOPIC_TP560 0x0000