linux/arch/mips/bcm63xx/boards/board_bcm963xx.c
Linus Torvalds de390bba79 Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
Pull MIPS update from Ralf Baechle:
 "This is the MIPS update for 3.7.

  A fair chunk of them are platform updates to the Cavium Octeon SOC
  (which involves machine generated header files of considerable size),
  Atheros ATH79xx, RMI aka Netlogic aka Broadcom XLP, Broadcom BCM63xx
  platforms.

  Support for the commercial MIPS simulator MIPSsim has been removed as
  MIPS Technologies is shifting away from this product and Qemu is
  offering various more powerful platforms.  The generic MIPS code can
  now also probe for no-execute / write-only TLB features implemented
  without the full SmartMIPS extension as permitted by the latest MIPS
  processor architecture.  Lots of small changes to generic code."

* 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus: (78 commits)
  MIPS: ath79: Fix CPU/DDR frequency calculation for SRIF PLLs
  MIPS: ath79: use correct fractional dividers for {CPU,DDR}_PLL on AR934x
  MIPS: BCM63XX: Properly handle mac address octet overflow
  MIPS: Kconfig: Avoid build errors by hiding USE_OF from the user.
  MIPS: Replace `-' in defconfig filename wth `_' for consistency.
  MIPS: Wire kcmp syscall.
  MIPS: MIPSsim: Remove the MIPSsim platform.
  MIPS: NOTIFY_RESUME is not needed in TIF masks
  MIPS: Merge the identical "return from syscall" per-ABI code
  MIPS: Unobfuscate _TIF..._MASK
  MIPS: Prevent hitting do_notify_resume() with !user_mode(regs).
  MIPS: Replace 'kernel_uses_smartmips_rixi' with 'cpu_has_rixi'.
  MIPS: Add base architecture support for RI and XI.
  MIPS: Optimise TLB handlers for MIPS32/64 R2 cores.
  MIPS: uasm: Add INS and EXT instructions.
  MIPS: Avoid pipeline stalls on some MIPS32R2 cores.
  MIPS: Make VPE count to be one-based.
  MIPS: Add new end of interrupt functionality for GIC.
  MIPS: Add EIC support for GIC.
  MIPS: Code clean-ups for the GIC.
  ...
2012-10-09 16:08:04 +09:00

930 lines
16 KiB
C

/*
* 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) 2008 Maxime Bizon <mbizon@freebox.fr>
* Copyright (C) 2008 Florian Fainelli <florian@openwrt.org>
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/ssb/ssb.h>
#include <asm/addrspace.h>
#include <bcm63xx_board.h>
#include <bcm63xx_cpu.h>
#include <bcm63xx_dev_uart.h>
#include <bcm63xx_regs.h>
#include <bcm63xx_io.h>
#include <bcm63xx_dev_pci.h>
#include <bcm63xx_dev_enet.h>
#include <bcm63xx_dev_dsp.h>
#include <bcm63xx_dev_flash.h>
#include <bcm63xx_dev_pcmcia.h>
#include <bcm63xx_dev_spi.h>
#include <bcm63xx_dev_usb_usbd.h>
#include <board_bcm963xx.h>
#define PFX "board_bcm963xx: "
static struct bcm963xx_nvram nvram;
static unsigned int mac_addr_used;
static struct board_info board;
/*
* known 6328 boards
*/
#ifdef CONFIG_BCM63XX_CPU_6328
static struct board_info __initdata board_96328avng = {
.name = "96328avng",
.expected_cpu_id = 0x6328,
.has_uart0 = 1,
.has_pci = 1,
.has_usbd = 0,
.usbd = {
.use_fullspeed = 0,
.port_no = 0,
},
.leds = {
{
.name = "96328avng::ppp-fail",
.gpio = 2,
.active_low = 1,
},
{
.name = "96328avng::power",
.gpio = 4,
.active_low = 1,
.default_trigger = "default-on",
},
{
.name = "96328avng::power-fail",
.gpio = 8,
.active_low = 1,
},
{
.name = "96328avng::wps",
.gpio = 9,
.active_low = 1,
},
{
.name = "96328avng::ppp",
.gpio = 11,
.active_low = 1,
},
},
};
#endif
/*
* known 6338 boards
*/
#ifdef CONFIG_BCM63XX_CPU_6338
static struct board_info __initdata board_96338gw = {
.name = "96338GW",
.expected_cpu_id = 0x6338,
.has_uart0 = 1,
.has_enet0 = 1,
.enet0 = {
.force_speed_100 = 1,
.force_duplex_full = 1,
},
.has_ohci0 = 1,
.leds = {
{
.name = "adsl",
.gpio = 3,
.active_low = 1,
},
{
.name = "ses",
.gpio = 5,
.active_low = 1,
},
{
.name = "ppp-fail",
.gpio = 4,
.active_low = 1,
},
{
.name = "power",
.gpio = 0,
.active_low = 1,
.default_trigger = "default-on",
},
{
.name = "stop",
.gpio = 1,
.active_low = 1,
}
},
};
static struct board_info __initdata board_96338w = {
.name = "96338W",
.expected_cpu_id = 0x6338,
.has_uart0 = 1,
.has_enet0 = 1,
.enet0 = {
.force_speed_100 = 1,
.force_duplex_full = 1,
},
.leds = {
{
.name = "adsl",
.gpio = 3,
.active_low = 1,
},
{
.name = "ses",
.gpio = 5,
.active_low = 1,
},
{
.name = "ppp-fail",
.gpio = 4,
.active_low = 1,
},
{
.name = "power",
.gpio = 0,
.active_low = 1,
.default_trigger = "default-on",
},
{
.name = "stop",
.gpio = 1,
.active_low = 1,
},
},
};
#endif
/*
* known 6345 boards
*/
#ifdef CONFIG_BCM63XX_CPU_6345
static struct board_info __initdata board_96345gw2 = {
.name = "96345GW2",
.expected_cpu_id = 0x6345,
.has_uart0 = 1,
};
#endif
/*
* known 6348 boards
*/
#ifdef CONFIG_BCM63XX_CPU_6348
static struct board_info __initdata board_96348r = {
.name = "96348R",
.expected_cpu_id = 0x6348,
.has_uart0 = 1,
.has_enet0 = 1,
.has_pci = 1,
.enet0 = {
.has_phy = 1,
.use_internal_phy = 1,
},
.leds = {
{
.name = "adsl-fail",
.gpio = 2,
.active_low = 1,
},
{
.name = "ppp",
.gpio = 3,
.active_low = 1,
},
{
.name = "ppp-fail",
.gpio = 4,
.active_low = 1,
},
{
.name = "power",
.gpio = 0,
.active_low = 1,
.default_trigger = "default-on",
},
{
.name = "stop",
.gpio = 1,
.active_low = 1,
},
},
};
static struct board_info __initdata board_96348gw_10 = {
.name = "96348GW-10",
.expected_cpu_id = 0x6348,
.has_uart0 = 1,
.has_enet0 = 1,
.has_enet1 = 1,
.has_pci = 1,
.enet0 = {
.has_phy = 1,
.use_internal_phy = 1,
},
.enet1 = {
.force_speed_100 = 1,
.force_duplex_full = 1,
},
.has_ohci0 = 1,
.has_pccard = 1,
.has_ehci0 = 1,
.has_dsp = 1,
.dsp = {
.gpio_rst = 6,
.gpio_int = 34,
.cs = 2,
.ext_irq = 2,
},
.leds = {
{
.name = "adsl-fail",
.gpio = 2,
.active_low = 1,
},
{
.name = "ppp",
.gpio = 3,
.active_low = 1,
},
{
.name = "ppp-fail",
.gpio = 4,
.active_low = 1,
},
{
.name = "power",
.gpio = 0,
.active_low = 1,
.default_trigger = "default-on",
},
{
.name = "stop",
.gpio = 1,
.active_low = 1,
},
},
};
static struct board_info __initdata board_96348gw_11 = {
.name = "96348GW-11",
.expected_cpu_id = 0x6348,
.has_uart0 = 1,
.has_enet0 = 1,
.has_enet1 = 1,
.has_pci = 1,
.enet0 = {
.has_phy = 1,
.use_internal_phy = 1,
},
.enet1 = {
.force_speed_100 = 1,
.force_duplex_full = 1,
},
.has_ohci0 = 1,
.has_pccard = 1,
.has_ehci0 = 1,
.leds = {
{
.name = "adsl-fail",
.gpio = 2,
.active_low = 1,
},
{
.name = "ppp",
.gpio = 3,
.active_low = 1,
},
{
.name = "ppp-fail",
.gpio = 4,
.active_low = 1,
},
{
.name = "power",
.gpio = 0,
.active_low = 1,
.default_trigger = "default-on",
},
{
.name = "stop",
.gpio = 1,
.active_low = 1,
},
},
};
static struct board_info __initdata board_96348gw = {
.name = "96348GW",
.expected_cpu_id = 0x6348,
.has_uart0 = 1,
.has_enet0 = 1,
.has_enet1 = 1,
.has_pci = 1,
.enet0 = {
.has_phy = 1,
.use_internal_phy = 1,
},
.enet1 = {
.force_speed_100 = 1,
.force_duplex_full = 1,
},
.has_ohci0 = 1,
.has_dsp = 1,
.dsp = {
.gpio_rst = 6,
.gpio_int = 34,
.ext_irq = 2,
.cs = 2,
},
.leds = {
{
.name = "adsl-fail",
.gpio = 2,
.active_low = 1,
},
{
.name = "ppp",
.gpio = 3,
.active_low = 1,
},
{
.name = "ppp-fail",
.gpio = 4,
.active_low = 1,
},
{
.name = "power",
.gpio = 0,
.active_low = 1,
.default_trigger = "default-on",
},
{
.name = "stop",
.gpio = 1,
.active_low = 1,
},
},
};
static struct board_info __initdata board_FAST2404 = {
.name = "F@ST2404",
.expected_cpu_id = 0x6348,
.has_uart0 = 1,
.has_enet0 = 1,
.has_enet1 = 1,
.has_pci = 1,
.enet0 = {
.has_phy = 1,
.use_internal_phy = 1,
},
.enet1 = {
.force_speed_100 = 1,
.force_duplex_full = 1,
},
.has_ohci0 = 1,
.has_pccard = 1,
.has_ehci0 = 1,
};
static struct board_info __initdata board_rta1025w_16 = {
.name = "RTA1025W_16",
.expected_cpu_id = 0x6348,
.has_enet0 = 1,
.has_enet1 = 1,
.has_pci = 1,
.enet0 = {
.has_phy = 1,
.use_internal_phy = 1,
},
.enet1 = {
.force_speed_100 = 1,
.force_duplex_full = 1,
},
};
static struct board_info __initdata board_DV201AMR = {
.name = "DV201AMR",
.expected_cpu_id = 0x6348,
.has_uart0 = 1,
.has_pci = 1,
.has_ohci0 = 1,
.has_enet0 = 1,
.has_enet1 = 1,
.enet0 = {
.has_phy = 1,
.use_internal_phy = 1,
},
.enet1 = {
.force_speed_100 = 1,
.force_duplex_full = 1,
},
};
static struct board_info __initdata board_96348gw_a = {
.name = "96348GW-A",
.expected_cpu_id = 0x6348,
.has_uart0 = 1,
.has_enet0 = 1,
.has_enet1 = 1,
.has_pci = 1,
.enet0 = {
.has_phy = 1,
.use_internal_phy = 1,
},
.enet1 = {
.force_speed_100 = 1,
.force_duplex_full = 1,
},
.has_ohci0 = 1,
};
#endif
/*
* known 6358 boards
*/
#ifdef CONFIG_BCM63XX_CPU_6358
static struct board_info __initdata board_96358vw = {
.name = "96358VW",
.expected_cpu_id = 0x6358,
.has_uart0 = 1,
.has_enet0 = 1,
.has_enet1 = 1,
.has_pci = 1,
.enet0 = {
.has_phy = 1,
.use_internal_phy = 1,
},
.enet1 = {
.force_speed_100 = 1,
.force_duplex_full = 1,
},
.has_ohci0 = 1,
.has_pccard = 1,
.has_ehci0 = 1,
.leds = {
{
.name = "adsl-fail",
.gpio = 15,
.active_low = 1,
},
{
.name = "ppp",
.gpio = 22,
.active_low = 1,
},
{
.name = "ppp-fail",
.gpio = 23,
.active_low = 1,
},
{
.name = "power",
.gpio = 4,
.default_trigger = "default-on",
},
{
.name = "stop",
.gpio = 5,
},
},
};
static struct board_info __initdata board_96358vw2 = {
.name = "96358VW2",
.expected_cpu_id = 0x6358,
.has_uart0 = 1,
.has_enet0 = 1,
.has_enet1 = 1,
.has_pci = 1,
.enet0 = {
.has_phy = 1,
.use_internal_phy = 1,
},
.enet1 = {
.force_speed_100 = 1,
.force_duplex_full = 1,
},
.has_ohci0 = 1,
.has_pccard = 1,
.has_ehci0 = 1,
.leds = {
{
.name = "adsl",
.gpio = 22,
.active_low = 1,
},
{
.name = "ppp-fail",
.gpio = 23,
},
{
.name = "power",
.gpio = 5,
.active_low = 1,
.default_trigger = "default-on",
},
{
.name = "stop",
.gpio = 4,
.active_low = 1,
},
},
};
static struct board_info __initdata board_AGPFS0 = {
.name = "AGPF-S0",
.expected_cpu_id = 0x6358,
.has_uart0 = 1,
.has_enet0 = 1,
.has_enet1 = 1,
.has_pci = 1,
.enet0 = {
.has_phy = 1,
.use_internal_phy = 1,
},
.enet1 = {
.force_speed_100 = 1,
.force_duplex_full = 1,
},
.has_ohci0 = 1,
.has_ehci0 = 1,
};
static struct board_info __initdata board_DWVS0 = {
.name = "DWV-S0",
.expected_cpu_id = 0x6358,
.has_enet0 = 1,
.has_enet1 = 1,
.has_pci = 1,
.enet0 = {
.has_phy = 1,
.use_internal_phy = 1,
},
.enet1 = {
.force_speed_100 = 1,
.force_duplex_full = 1,
},
.has_ohci0 = 1,
};
#endif
/*
* all boards
*/
static const struct board_info __initconst *bcm963xx_boards[] = {
#ifdef CONFIG_BCM63XX_CPU_6328
&board_96328avng,
#endif
#ifdef CONFIG_BCM63XX_CPU_6338
&board_96338gw,
&board_96338w,
#endif
#ifdef CONFIG_BCM63XX_CPU_6345
&board_96345gw2,
#endif
#ifdef CONFIG_BCM63XX_CPU_6348
&board_96348r,
&board_96348gw,
&board_96348gw_10,
&board_96348gw_11,
&board_FAST2404,
&board_DV201AMR,
&board_96348gw_a,
&board_rta1025w_16,
#endif
#ifdef CONFIG_BCM63XX_CPU_6358
&board_96358vw,
&board_96358vw2,
&board_AGPFS0,
&board_DWVS0,
#endif
};
/*
* Register a sane SPROMv2 to make the on-board
* bcm4318 WLAN work
*/
#ifdef CONFIG_SSB_PCIHOST
static struct ssb_sprom bcm63xx_sprom = {
.revision = 0x02,
.board_rev = 0x17,
.country_code = 0x0,
.ant_available_bg = 0x3,
.pa0b0 = 0x15ae,
.pa0b1 = 0xfa85,
.pa0b2 = 0xfe8d,
.pa1b0 = 0xffff,
.pa1b1 = 0xffff,
.pa1b2 = 0xffff,
.gpio0 = 0xff,
.gpio1 = 0xff,
.gpio2 = 0xff,
.gpio3 = 0xff,
.maxpwr_bg = 0x004c,
.itssi_bg = 0x00,
.boardflags_lo = 0x2848,
.boardflags_hi = 0x0000,
};
int bcm63xx_get_fallback_sprom(struct ssb_bus *bus, struct ssb_sprom *out)
{
if (bus->bustype == SSB_BUSTYPE_PCI) {
memcpy(out, &bcm63xx_sprom, sizeof(struct ssb_sprom));
return 0;
} else {
printk(KERN_ERR PFX "unable to fill SPROM for given bustype.\n");
return -EINVAL;
}
}
#endif
/*
* return board name for /proc/cpuinfo
*/
const char *board_get_name(void)
{
return board.name;
}
/*
* register & return a new board mac address
*/
static int board_get_mac_address(u8 *mac)
{
u8 *oui;
int count;
if (mac_addr_used >= nvram.mac_addr_count) {
printk(KERN_ERR PFX "not enough mac address\n");
return -ENODEV;
}
memcpy(mac, nvram.mac_addr_base, ETH_ALEN);
oui = mac + ETH_ALEN/2 - 1;
count = mac_addr_used;
while (count--) {
u8 *p = mac + ETH_ALEN - 1;
do {
(*p)++;
if (*p != 0)
break;
p--;
} while (p != oui);
if (p == oui) {
printk(KERN_ERR PFX "unable to fetch mac address\n");
return -ENODEV;
}
}
mac_addr_used++;
return 0;
}
/*
* early init callback, read nvram data from flash and checksum it
*/
void __init board_prom_init(void)
{
unsigned int check_len, i;
u8 *boot_addr, *cfe, *p;
char cfe_version[32];
u32 val;
/* read base address of boot chip select (0)
* 6328 does not have MPI but boots from a fixed address
*/
if (BCMCPU_IS_6328())
val = 0x18000000;
else {
val = bcm_mpi_readl(MPI_CSBASE_REG(0));
val &= MPI_CSBASE_BASE_MASK;
}
boot_addr = (u8 *)KSEG1ADDR(val);
/* dump cfe version */
cfe = boot_addr + BCM963XX_CFE_VERSION_OFFSET;
if (!memcmp(cfe, "cfe-v", 5))
snprintf(cfe_version, sizeof(cfe_version), "%u.%u.%u-%u.%u",
cfe[5], cfe[6], cfe[7], cfe[8], cfe[9]);
else
strcpy(cfe_version, "unknown");
printk(KERN_INFO PFX "CFE version: %s\n", cfe_version);
/* extract nvram data */
memcpy(&nvram, boot_addr + BCM963XX_NVRAM_OFFSET, sizeof(nvram));
/* check checksum before using data */
if (nvram.version <= 4)
check_len = offsetof(struct bcm963xx_nvram, checksum_old);
else
check_len = sizeof(nvram);
val = 0;
p = (u8 *)&nvram;
while (check_len--)
val += *p;
if (val) {
printk(KERN_ERR PFX "invalid nvram checksum\n");
return;
}
/* find board by name */
for (i = 0; i < ARRAY_SIZE(bcm963xx_boards); i++) {
if (strncmp(nvram.name, bcm963xx_boards[i]->name,
sizeof(nvram.name)))
continue;
/* copy, board desc array is marked initdata */
memcpy(&board, bcm963xx_boards[i], sizeof(board));
break;
}
/* bail out if board is not found, will complain later */
if (!board.name[0]) {
char name[17];
memcpy(name, nvram.name, 16);
name[16] = 0;
printk(KERN_ERR PFX "unknown bcm963xx board: %s\n",
name);
return;
}
/* setup pin multiplexing depending on board enabled device,
* this has to be done this early since PCI init is done
* inside arch_initcall */
val = 0;
#ifdef CONFIG_PCI
if (board.has_pci) {
bcm63xx_pci_enabled = 1;
if (BCMCPU_IS_6348())
val |= GPIO_MODE_6348_G2_PCI;
}
#endif
if (board.has_pccard) {
if (BCMCPU_IS_6348())
val |= GPIO_MODE_6348_G1_MII_PCCARD;
}
if (board.has_enet0 && !board.enet0.use_internal_phy) {
if (BCMCPU_IS_6348())
val |= GPIO_MODE_6348_G3_EXT_MII |
GPIO_MODE_6348_G0_EXT_MII;
}
if (board.has_enet1 && !board.enet1.use_internal_phy) {
if (BCMCPU_IS_6348())
val |= GPIO_MODE_6348_G3_EXT_MII |
GPIO_MODE_6348_G0_EXT_MII;
}
bcm_gpio_writel(val, GPIO_MODE_REG);
}
/*
* second stage init callback, good time to panic if we couldn't
* identify on which board we're running since early printk is working
*/
void __init board_setup(void)
{
if (!board.name[0])
panic("unable to detect bcm963xx board");
printk(KERN_INFO PFX "board name: %s\n", board.name);
/* make sure we're running on expected cpu */
if (bcm63xx_get_cpu_id() != board.expected_cpu_id)
panic("unexpected CPU for bcm963xx board");
}
static struct gpio_led_platform_data bcm63xx_led_data;
static struct platform_device bcm63xx_gpio_leds = {
.name = "leds-gpio",
.id = 0,
.dev.platform_data = &bcm63xx_led_data,
};
/*
* third stage init callback, register all board devices.
*/
int __init board_register_devices(void)
{
if (board.has_uart0)
bcm63xx_uart_register(0);
if (board.has_uart1)
bcm63xx_uart_register(1);
if (board.has_pccard)
bcm63xx_pcmcia_register();
if (board.has_enet0 &&
!board_get_mac_address(board.enet0.mac_addr))
bcm63xx_enet_register(0, &board.enet0);
if (board.has_enet1 &&
!board_get_mac_address(board.enet1.mac_addr))
bcm63xx_enet_register(1, &board.enet1);
if (board.has_usbd)
bcm63xx_usbd_register(&board.usbd);
if (board.has_dsp)
bcm63xx_dsp_register(&board.dsp);
/* Generate MAC address for WLAN and register our SPROM,
* do this after registering enet devices
*/
#ifdef CONFIG_SSB_PCIHOST
if (!board_get_mac_address(bcm63xx_sprom.il0mac)) {
memcpy(bcm63xx_sprom.et0mac, bcm63xx_sprom.il0mac, ETH_ALEN);
memcpy(bcm63xx_sprom.et1mac, bcm63xx_sprom.il0mac, ETH_ALEN);
if (ssb_arch_register_fallback_sprom(
&bcm63xx_get_fallback_sprom) < 0)
pr_err(PFX "failed to register fallback SPROM\n");
}
#endif
bcm63xx_spi_register();
bcm63xx_flash_register();
bcm63xx_led_data.num_leds = ARRAY_SIZE(board.leds);
bcm63xx_led_data.leds = board.leds;
platform_device_register(&bcm63xx_gpio_leds);
return 0;
}