diff options
Diffstat (limited to 'arch/mips/bcm63xx')
26 files changed, 4547 insertions, 0 deletions
diff --git a/arch/mips/bcm63xx/Kconfig b/arch/mips/bcm63xx/Kconfig new file mode 100644 index 000000000..837f6e5a2 --- /dev/null +++ b/arch/mips/bcm63xx/Kconfig @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: GPL-2.0 +menu "CPU support" + depends on BCM63XX + +config BCM63XX_CPU_3368 + bool "support 3368 CPU" + select SYS_HAS_CPU_BMIPS4350 + select HAVE_PCI + +config BCM63XX_CPU_6328 + bool "support 6328 CPU" + select SYS_HAS_CPU_BMIPS4350 + select HAVE_PCI + +config BCM63XX_CPU_6338 + bool "support 6338 CPU" + select SYS_HAS_CPU_BMIPS32_3300 + select HAVE_PCI + +config BCM63XX_CPU_6345 + bool "support 6345 CPU" + select SYS_HAS_CPU_BMIPS32_3300 + +config BCM63XX_CPU_6348 + bool "support 6348 CPU" + select SYS_HAS_CPU_BMIPS32_3300 + select HAVE_PCI + +config BCM63XX_CPU_6358 + bool "support 6358 CPU" + select SYS_HAS_CPU_BMIPS4350 + select HAVE_PCI + +config BCM63XX_CPU_6362 + bool "support 6362 CPU" + select SYS_HAS_CPU_BMIPS4350 + select HAVE_PCI + +config BCM63XX_CPU_6368 + bool "support 6368 CPU" + select SYS_HAS_CPU_BMIPS4350 + select HAVE_PCI +endmenu + +source "arch/mips/bcm63xx/boards/Kconfig" diff --git a/arch/mips/bcm63xx/Makefile b/arch/mips/bcm63xx/Makefile new file mode 100644 index 000000000..d89651e53 --- /dev/null +++ b/arch/mips/bcm63xx/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-y += clk.o cpu.o cs.o gpio.o irq.o nvram.o prom.o reset.o \ + setup.o timer.o dev-enet.o dev-flash.o dev-pcmcia.o \ + dev-rng.o dev-spi.o dev-hsspi.o dev-uart.o dev-wdt.o \ + dev-usb-usbd.o +obj-$(CONFIG_EARLY_PRINTK) += early_printk.o + +obj-y += boards/ diff --git a/arch/mips/bcm63xx/Platform b/arch/mips/bcm63xx/Platform new file mode 100644 index 000000000..882dc40f4 --- /dev/null +++ b/arch/mips/bcm63xx/Platform @@ -0,0 +1,6 @@ +# +# Broadcom BCM63XX boards +# +cflags-$(CONFIG_BCM63XX) += \ + -I$(srctree)/arch/mips/include/asm/mach-bcm63xx/ +load-$(CONFIG_BCM63XX) := 0xffffffff80010000 diff --git a/arch/mips/bcm63xx/boards/Kconfig b/arch/mips/bcm63xx/boards/Kconfig new file mode 100644 index 000000000..492c3bd00 --- /dev/null +++ b/arch/mips/bcm63xx/boards/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +choice + prompt "Board support" + depends on BCM63XX + default BOARD_BCM963XX + +config BOARD_BCM963XX + bool "Generic Broadcom 963xx boards" + select SSB + +endchoice diff --git a/arch/mips/bcm63xx/boards/Makefile b/arch/mips/bcm63xx/boards/Makefile new file mode 100644 index 000000000..a74b9c8d0 --- /dev/null +++ b/arch/mips/bcm63xx/boards/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_BOARD_BCM963XX) += board_bcm963xx.o diff --git a/arch/mips/bcm63xx/boards/board_bcm963xx.c b/arch/mips/bcm63xx/boards/board_bcm963xx.c new file mode 100644 index 000000000..01aff80a5 --- /dev/null +++ b/arch/mips/bcm63xx/boards/board_bcm963xx.c @@ -0,0 +1,911 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> + * Copyright (C) 2008 Florian Fainelli <florian@openwrt.org> + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#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_nvram.h> +#include <bcm63xx_dev_pci.h> +#include <bcm63xx_dev_enet.h> +#include <bcm63xx_dev_flash.h> +#include <bcm63xx_dev_hsspi.h> +#include <bcm63xx_dev_pcmcia.h> +#include <bcm63xx_dev_spi.h> +#include <bcm63xx_dev_usb_usbd.h> +#include <board_bcm963xx.h> + +#include <uapi/linux/bcm933xx_hcs.h> + +#define HCS_OFFSET_128K 0x20000 + +static struct board_info board; + +/* + * known 3368 boards + */ +#ifdef CONFIG_BCM63XX_CPU_3368 +static struct board_info __initdata board_cvg834g = { + .name = "CVG834G_E15R3921", + .expected_cpu_id = 0x3368, + + .ephy_reset_gpio = 36, + .ephy_reset_gpio_flags = GPIOF_INIT_HIGH, + .has_pci = 1, + .has_uart0 = 1, + .has_uart1 = 1, + + .has_enet0 = 1, + .enet0 = { + .has_phy = 1, + .use_internal_phy = 1, + }, + + .leds = { + { + .name = "CVG834G:green:power", + .gpio = 37, + .default_trigger= "default-on", + }, + }, +}; +#endif /* CONFIG_BCM63XX_CPU_3368 */ + +/* + * known 6328 boards + */ +#ifdef CONFIG_BCM63XX_CPU_6328 +static struct board_info __initdata board_96328avng = { + .name = "96328avng", + .expected_cpu_id = 0x6328, + + .has_pci = 1, + .has_uart0 = 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 /* CONFIG_BCM63XX_CPU_6328 */ + +/* + * known 6338 boards + */ +#ifdef CONFIG_BCM63XX_CPU_6338 +static struct board_info __initdata board_96338gw = { + .name = "96338GW", + .expected_cpu_id = 0x6338, + + .has_ohci0 = 1, + .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, + } + }, +}; + +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 /* CONFIG_BCM63XX_CPU_6338 */ + +/* + * 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 /* CONFIG_BCM63XX_CPU_6345 */ + +/* + * known 6348 boards + */ +#ifdef CONFIG_BCM63XX_CPU_6348 +static struct board_info __initdata board_96348r = { + .name = "96348R", + .expected_cpu_id = 0x6348, + + .has_pci = 1, + .has_uart0 = 1, + + .has_enet0 = 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_ohci0 = 1, + .has_pccard = 1, + .has_pci = 1, + .has_uart0 = 1, + + .has_enet0 = 1, + .enet0 = { + .has_phy = 1, + .use_internal_phy = 1, + }, + + .has_enet1 = 1, + .enet1 = { + .force_speed_100 = 1, + .force_duplex_full = 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_11 = { + .name = "96348GW-11", + .expected_cpu_id = 0x6348, + + .has_ohci0 = 1, + .has_pccard = 1, + .has_pci = 1, + .has_uart0 = 1, + + .has_enet0 = 1, + .enet0 = { + .has_phy = 1, + .use_internal_phy = 1, + }, + + .has_enet1 = 1, + .enet1 = { + .force_speed_100 = 1, + .force_duplex_full = 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_ohci0 = 1, + .has_pci = 1, + .has_uart0 = 1, + + .has_enet0 = 1, + .enet0 = { + .has_phy = 1, + .use_internal_phy = 1, + }, + + .has_enet1 = 1, + .enet1 = { + .force_speed_100 = 1, + .force_duplex_full = 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_FAST2404 = { + .name = "F@ST2404", + .expected_cpu_id = 0x6348, + + .has_ohci0 = 1, + .has_pccard = 1, + .has_pci = 1, + .has_uart0 = 1, + + .has_enet0 = 1, + .enet0 = { + .has_phy = 1, + .use_internal_phy = 1, + }, + + .has_enet1 = 1, + .enet1 = { + .force_speed_100 = 1, + .force_duplex_full = 1, + }, +}; + +static struct board_info __initdata board_rta1025w_16 = { + .name = "RTA1025W_16", + .expected_cpu_id = 0x6348, + + .has_pci = 1, + + .has_enet0 = 1, + .enet0 = { + .has_phy = 1, + .use_internal_phy = 1, + }, + + .has_enet1 = 1, + .enet1 = { + .force_speed_100 = 1, + .force_duplex_full = 1, + }, +}; + +static struct board_info __initdata board_DV201AMR = { + .name = "DV201AMR", + .expected_cpu_id = 0x6348, + + .has_ohci0 = 1, + .has_pci = 1, + .has_uart0 = 1, + + .has_enet0 = 1, + .enet0 = { + .has_phy = 1, + .use_internal_phy = 1, + }, + + .has_enet1 = 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_ohci0 = 1, + .has_pci = 1, + .has_uart0 = 1, + + .has_enet0 = 1, + .enet0 = { + .has_phy = 1, + .use_internal_phy = 1, + }, + + .has_enet1 = 1, + .enet1 = { + .force_speed_100 = 1, + .force_duplex_full = 1, + }, +}; +#endif /* CONFIG_BCM63XX_CPU_6348 */ + +/* + * known 6358 boards + */ +#ifdef CONFIG_BCM63XX_CPU_6358 +static struct board_info __initdata board_96358vw = { + .name = "96358VW", + .expected_cpu_id = 0x6358, + + .has_ehci0 = 1, + .has_ohci0 = 1, + .has_pccard = 1, + .has_pci = 1, + .has_uart0 = 1, + + .has_enet0 = 1, + .enet0 = { + .has_phy = 1, + .use_internal_phy = 1, + }, + + .has_enet1 = 1, + .enet1 = { + .force_speed_100 = 1, + .force_duplex_full = 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_ehci0 = 1, + .has_ohci0 = 1, + .has_pccard = 1, + .has_pci = 1, + .has_uart0 = 1, + + .has_enet0 = 1, + .enet0 = { + .has_phy = 1, + .use_internal_phy = 1, + }, + + .has_enet1 = 1, + .enet1 = { + .force_speed_100 = 1, + .force_duplex_full = 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_ehci0 = 1, + .has_ohci0 = 1, + .has_pci = 1, + .has_uart0 = 1, + + .has_enet0 = 1, + .enet0 = { + .has_phy = 1, + .use_internal_phy = 1, + }, + + .has_enet1 = 1, + .enet1 = { + .force_speed_100 = 1, + .force_duplex_full = 1, + }, +}; + +static struct board_info __initdata board_DWVS0 = { + .name = "DWV-S0", + .expected_cpu_id = 0x6358, + + .has_ehci0 = 1, + .has_ohci0 = 1, + .has_pci = 1, + + .has_enet0 = 1, + .enet0 = { + .has_phy = 1, + .use_internal_phy = 1, + }, + + .has_enet1 = 1, + .enet1 = { + .force_speed_100 = 1, + .force_duplex_full = 1, + }, +}; +#endif /* CONFIG_BCM63XX_CPU_6358 */ + +/* + * all boards + */ +static const struct board_info __initconst *bcm963xx_boards[] = { +#ifdef CONFIG_BCM63XX_CPU_3368 + &board_cvg834g, +#endif /* CONFIG_BCM63XX_CPU_3368 */ +#ifdef CONFIG_BCM63XX_CPU_6328 + &board_96328avng, +#endif /* CONFIG_BCM63XX_CPU_6328 */ +#ifdef CONFIG_BCM63XX_CPU_6338 + &board_96338gw, + &board_96338w, +#endif /* CONFIG_BCM63XX_CPU_6338 */ +#ifdef CONFIG_BCM63XX_CPU_6345 + &board_96345gw2, +#endif /* CONFIG_BCM63XX_CPU_6345 */ +#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 /* CONFIG_BCM63XX_CPU_6348 */ +#ifdef CONFIG_BCM63XX_CPU_6358 + &board_96358vw, + &board_96358vw2, + &board_AGPFS0, + &board_DWVS0, +#endif /* CONFIG_BCM63XX_CPU_6358 */ +}; + +/* + * 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 { + pr_err("unable to fill SPROM for given bustype\n"); + return -EINVAL; + } +} +#endif /* CONFIG_SSB_PCIHOST */ + +/* + * return board name for /proc/cpuinfo + */ +const char *board_get_name(void) +{ + return board.name; +} + +/* + * early init callback, read nvram data from flash and checksum it + */ +void __init board_prom_init(void) +{ + unsigned int i; + u8 *boot_addr, *cfe; + char cfe_version[32]; + char *board_name = NULL; + u32 val; + struct bcm_hcs *hcs; + + /* read base address of boot chip select (0) + * 6328/6362 do not have MPI but boot from a fixed address + */ + if (BCMCPU_IS_6328() || BCMCPU_IS_6362()) { + 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 (strstarts(cfe, "cfe-")) { + if(cfe[4] == 'v') { + if(cfe[5] == 'd') + snprintf(cfe_version, 11, "%s", + (char *) &cfe[5]); + else if (cfe[10] > 0) + snprintf(cfe_version, sizeof(cfe_version), + "%u.%u.%u-%u.%u-%u", cfe[5], cfe[6], + cfe[7], cfe[8], cfe[9], cfe[10]); + else + snprintf(cfe_version, sizeof(cfe_version), + "%u.%u.%u-%u.%u", cfe[5], cfe[6], + cfe[7], cfe[8], cfe[9]); + } else { + snprintf(cfe_version, 12, "%s", (char *) &cfe[4]); + } + } else { + strcpy(cfe_version, "unknown"); + } + pr_info("CFE version: %s\n", cfe_version); + + bcm63xx_nvram_init(boot_addr + BCM963XX_NVRAM_OFFSET); + + if (BCMCPU_IS_3368()) { + hcs = (struct bcm_hcs *)boot_addr; + board_name = hcs->filename; + } else { + board_name = bcm63xx_nvram_get_name(); + } + /* find board by name */ + for (i = 0; i < ARRAY_SIZE(bcm963xx_boards); i++) { + if (strncmp(board_name, bcm963xx_boards[i]->name, 16)) + 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, board_name, 16); + name[16] = 0; + pr_err("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 /* CONFIG_PCI */ + + 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"); + pr_info("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 && + !bcm63xx_nvram_get_mac_address(board.enet0.mac_addr)) + bcm63xx_enet_register(0, &board.enet0); + + if (board.has_enet1 && + !bcm63xx_nvram_get_mac_address(board.enet1.mac_addr)) + bcm63xx_enet_register(1, &board.enet1); + + if (board.has_enetsw && + !bcm63xx_nvram_get_mac_address(board.enetsw.mac_addr)) + bcm63xx_enetsw_register(&board.enetsw); + + if (board.has_usbd) + bcm63xx_usbd_register(&board.usbd); + + /* Generate MAC address for WLAN and register our SPROM, + * do this after registering enet devices + */ +#ifdef CONFIG_SSB_PCIHOST + if (!bcm63xx_nvram_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("failed to register fallback SPROM\n"); + } +#endif /* CONFIG_SSB_PCIHOST */ + + bcm63xx_spi_register(); + + bcm63xx_hsspi_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); + + if (board.ephy_reset_gpio && board.ephy_reset_gpio_flags) + gpio_request_one(board.ephy_reset_gpio, + board.ephy_reset_gpio_flags, "ephy-reset"); + + return 0; +} diff --git a/arch/mips/bcm63xx/clk.c b/arch/mips/bcm63xx/clk.c new file mode 100644 index 000000000..86a6e2590 --- /dev/null +++ b/arch/mips/bcm63xx/clk.c @@ -0,0 +1,579 @@ +/* + * 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> + */ + +#include <linux/init.h> +#include <linux/export.h> +#include <linux/mutex.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/delay.h> +#include <bcm63xx_cpu.h> +#include <bcm63xx_io.h> +#include <bcm63xx_regs.h> +#include <bcm63xx_reset.h> + +struct clk { + void (*set)(struct clk *, int); + unsigned int rate; + unsigned int usage; + int id; +}; + +static DEFINE_MUTEX(clocks_mutex); + + +static void clk_enable_unlocked(struct clk *clk) +{ + if (clk->set && (clk->usage++) == 0) + clk->set(clk, 1); +} + +static void clk_disable_unlocked(struct clk *clk) +{ + if (clk->set && (--clk->usage) == 0) + clk->set(clk, 0); +} + +static void bcm_hwclock_set(u32 mask, int enable) +{ + u32 reg; + + reg = bcm_perf_readl(PERF_CKCTL_REG); + if (enable) + reg |= mask; + else + reg &= ~mask; + bcm_perf_writel(reg, PERF_CKCTL_REG); +} + +/* + * Ethernet MAC "misc" clock: dma clocks and main clock on 6348 + */ +static void enet_misc_set(struct clk *clk, int enable) +{ + u32 mask; + + if (BCMCPU_IS_6338()) + mask = CKCTL_6338_ENET_EN; + else if (BCMCPU_IS_6345()) + mask = CKCTL_6345_ENET_EN; + else if (BCMCPU_IS_6348()) + mask = CKCTL_6348_ENET_EN; + else + /* BCMCPU_IS_6358 */ + mask = CKCTL_6358_EMUSB_EN; + bcm_hwclock_set(mask, enable); +} + +static struct clk clk_enet_misc = { + .set = enet_misc_set, +}; + +/* + * Ethernet MAC clocks: only relevant on 6358, silently enable misc + * clocks + */ +static void enetx_set(struct clk *clk, int enable) +{ + if (enable) + clk_enable_unlocked(&clk_enet_misc); + else + clk_disable_unlocked(&clk_enet_misc); + + if (BCMCPU_IS_3368() || BCMCPU_IS_6358()) { + u32 mask; + + if (clk->id == 0) + mask = CKCTL_6358_ENET0_EN; + else + mask = CKCTL_6358_ENET1_EN; + bcm_hwclock_set(mask, enable); + } +} + +static struct clk clk_enet0 = { + .id = 0, + .set = enetx_set, +}; + +static struct clk clk_enet1 = { + .id = 1, + .set = enetx_set, +}; + +/* + * Ethernet PHY clock + */ +static void ephy_set(struct clk *clk, int enable) +{ + if (BCMCPU_IS_3368() || BCMCPU_IS_6358()) + bcm_hwclock_set(CKCTL_6358_EPHY_EN, enable); +} + + +static struct clk clk_ephy = { + .set = ephy_set, +}; + +/* + * Ethernet switch SAR clock + */ +static void swpkt_sar_set(struct clk *clk, int enable) +{ + if (BCMCPU_IS_6368()) + bcm_hwclock_set(CKCTL_6368_SWPKT_SAR_EN, enable); + else + return; +} + +static struct clk clk_swpkt_sar = { + .set = swpkt_sar_set, +}; + +/* + * Ethernet switch USB clock + */ +static void swpkt_usb_set(struct clk *clk, int enable) +{ + if (BCMCPU_IS_6368()) + bcm_hwclock_set(CKCTL_6368_SWPKT_USB_EN, enable); + else + return; +} + +static struct clk clk_swpkt_usb = { + .set = swpkt_usb_set, +}; + +/* + * Ethernet switch clock + */ +static void enetsw_set(struct clk *clk, int enable) +{ + if (BCMCPU_IS_6328()) { + bcm_hwclock_set(CKCTL_6328_ROBOSW_EN, enable); + } else if (BCMCPU_IS_6362()) { + bcm_hwclock_set(CKCTL_6362_ROBOSW_EN, enable); + } else if (BCMCPU_IS_6368()) { + if (enable) { + clk_enable_unlocked(&clk_swpkt_sar); + clk_enable_unlocked(&clk_swpkt_usb); + } else { + clk_disable_unlocked(&clk_swpkt_usb); + clk_disable_unlocked(&clk_swpkt_sar); + } + bcm_hwclock_set(CKCTL_6368_ROBOSW_EN, enable); + } else { + return; + } + + if (enable) { + /* reset switch core afer clock change */ + bcm63xx_core_set_reset(BCM63XX_RESET_ENETSW, 1); + msleep(10); + bcm63xx_core_set_reset(BCM63XX_RESET_ENETSW, 0); + msleep(10); + } +} + +static struct clk clk_enetsw = { + .set = enetsw_set, +}; + +/* + * PCM clock + */ +static void pcm_set(struct clk *clk, int enable) +{ + if (BCMCPU_IS_3368()) + bcm_hwclock_set(CKCTL_3368_PCM_EN, enable); + if (BCMCPU_IS_6358()) + bcm_hwclock_set(CKCTL_6358_PCM_EN, enable); +} + +static struct clk clk_pcm = { + .set = pcm_set, +}; + +/* + * USB host clock + */ +static void usbh_set(struct clk *clk, int enable) +{ + if (BCMCPU_IS_6328()) + bcm_hwclock_set(CKCTL_6328_USBH_EN, enable); + else if (BCMCPU_IS_6348()) + bcm_hwclock_set(CKCTL_6348_USBH_EN, enable); + else if (BCMCPU_IS_6362()) + bcm_hwclock_set(CKCTL_6362_USBH_EN, enable); + else if (BCMCPU_IS_6368()) + bcm_hwclock_set(CKCTL_6368_USBH_EN, enable); +} + +static struct clk clk_usbh = { + .set = usbh_set, +}; + +/* + * USB device clock + */ +static void usbd_set(struct clk *clk, int enable) +{ + if (BCMCPU_IS_6328()) + bcm_hwclock_set(CKCTL_6328_USBD_EN, enable); + else if (BCMCPU_IS_6362()) + bcm_hwclock_set(CKCTL_6362_USBD_EN, enable); + else if (BCMCPU_IS_6368()) + bcm_hwclock_set(CKCTL_6368_USBD_EN, enable); +} + +static struct clk clk_usbd = { + .set = usbd_set, +}; + +/* + * SPI clock + */ +static void spi_set(struct clk *clk, int enable) +{ + u32 mask; + + if (BCMCPU_IS_6338()) + mask = CKCTL_6338_SPI_EN; + else if (BCMCPU_IS_6348()) + mask = CKCTL_6348_SPI_EN; + else if (BCMCPU_IS_3368() || BCMCPU_IS_6358()) + mask = CKCTL_6358_SPI_EN; + else if (BCMCPU_IS_6362()) + mask = CKCTL_6362_SPI_EN; + else + /* BCMCPU_IS_6368 */ + mask = CKCTL_6368_SPI_EN; + bcm_hwclock_set(mask, enable); +} + +static struct clk clk_spi = { + .set = spi_set, +}; + +/* + * HSSPI clock + */ +static void hsspi_set(struct clk *clk, int enable) +{ + u32 mask; + + if (BCMCPU_IS_6328()) + mask = CKCTL_6328_HSSPI_EN; + else if (BCMCPU_IS_6362()) + mask = CKCTL_6362_HSSPI_EN; + else + return; + + bcm_hwclock_set(mask, enable); +} + +static struct clk clk_hsspi = { + .set = hsspi_set, +}; + +/* + * HSSPI PLL + */ +static struct clk clk_hsspi_pll; + +/* + * XTM clock + */ +static void xtm_set(struct clk *clk, int enable) +{ + if (!BCMCPU_IS_6368()) + return; + + if (enable) + clk_enable_unlocked(&clk_swpkt_sar); + else + clk_disable_unlocked(&clk_swpkt_sar); + + bcm_hwclock_set(CKCTL_6368_SAR_EN, enable); + + if (enable) { + /* reset sar core afer clock change */ + bcm63xx_core_set_reset(BCM63XX_RESET_SAR, 1); + mdelay(1); + bcm63xx_core_set_reset(BCM63XX_RESET_SAR, 0); + mdelay(1); + } +} + + +static struct clk clk_xtm = { + .set = xtm_set, +}; + +/* + * IPsec clock + */ +static void ipsec_set(struct clk *clk, int enable) +{ + if (BCMCPU_IS_6362()) + bcm_hwclock_set(CKCTL_6362_IPSEC_EN, enable); + else if (BCMCPU_IS_6368()) + bcm_hwclock_set(CKCTL_6368_IPSEC_EN, enable); +} + +static struct clk clk_ipsec = { + .set = ipsec_set, +}; + +/* + * PCIe clock + */ + +static void pcie_set(struct clk *clk, int enable) +{ + if (BCMCPU_IS_6328()) + bcm_hwclock_set(CKCTL_6328_PCIE_EN, enable); + else if (BCMCPU_IS_6362()) + bcm_hwclock_set(CKCTL_6362_PCIE_EN, enable); +} + +static struct clk clk_pcie = { + .set = pcie_set, +}; + +/* + * Internal peripheral clock + */ +static struct clk clk_periph = { + .rate = (50 * 1000 * 1000), +}; + + +/* + * Linux clock API implementation + */ +int clk_enable(struct clk *clk) +{ + if (!clk) + return 0; + mutex_lock(&clocks_mutex); + clk_enable_unlocked(clk); + mutex_unlock(&clocks_mutex); + return 0; +} + +EXPORT_SYMBOL(clk_enable); + +void clk_disable(struct clk *clk) +{ + if (!clk) + return; + + mutex_lock(&clocks_mutex); + clk_disable_unlocked(clk); + mutex_unlock(&clocks_mutex); +} + +EXPORT_SYMBOL(clk_disable); + +struct clk *clk_get_parent(struct clk *clk) +{ + return NULL; +} +EXPORT_SYMBOL(clk_get_parent); + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + return 0; +} +EXPORT_SYMBOL(clk_set_parent); + +unsigned long clk_get_rate(struct clk *clk) +{ + if (!clk) + return 0; + + return clk->rate; +} + +EXPORT_SYMBOL(clk_get_rate); + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + return 0; +} +EXPORT_SYMBOL_GPL(clk_set_rate); + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + return 0; +} +EXPORT_SYMBOL_GPL(clk_round_rate); + +static struct clk_lookup bcm3368_clks[] = { + /* fixed rate clocks */ + CLKDEV_INIT(NULL, "periph", &clk_periph), + CLKDEV_INIT("bcm63xx_uart.0", "refclk", &clk_periph), + CLKDEV_INIT("bcm63xx_uart.1", "refclk", &clk_periph), + /* gated clocks */ + CLKDEV_INIT(NULL, "enet0", &clk_enet0), + CLKDEV_INIT(NULL, "enet1", &clk_enet1), + CLKDEV_INIT(NULL, "ephy", &clk_ephy), + CLKDEV_INIT(NULL, "usbh", &clk_usbh), + CLKDEV_INIT(NULL, "usbd", &clk_usbd), + CLKDEV_INIT(NULL, "spi", &clk_spi), + CLKDEV_INIT(NULL, "pcm", &clk_pcm), + CLKDEV_INIT("bcm63xx_enet.0", "enet", &clk_enet0), + CLKDEV_INIT("bcm63xx_enet.1", "enet", &clk_enet1), +}; + +static struct clk_lookup bcm6328_clks[] = { + /* fixed rate clocks */ + CLKDEV_INIT(NULL, "periph", &clk_periph), + CLKDEV_INIT("bcm63xx_uart.0", "refclk", &clk_periph), + CLKDEV_INIT("bcm63xx_uart.1", "refclk", &clk_periph), + CLKDEV_INIT("bcm63xx-hsspi.0", "pll", &clk_hsspi_pll), + /* gated clocks */ + CLKDEV_INIT(NULL, "enetsw", &clk_enetsw), + CLKDEV_INIT(NULL, "usbh", &clk_usbh), + CLKDEV_INIT(NULL, "usbd", &clk_usbd), + CLKDEV_INIT(NULL, "hsspi", &clk_hsspi), + CLKDEV_INIT(NULL, "pcie", &clk_pcie), +}; + +static struct clk_lookup bcm6338_clks[] = { + /* fixed rate clocks */ + CLKDEV_INIT(NULL, "periph", &clk_periph), + CLKDEV_INIT("bcm63xx_uart.0", "refclk", &clk_periph), + /* gated clocks */ + CLKDEV_INIT(NULL, "enet0", &clk_enet0), + CLKDEV_INIT(NULL, "enet1", &clk_enet1), + CLKDEV_INIT(NULL, "ephy", &clk_ephy), + CLKDEV_INIT(NULL, "usbh", &clk_usbh), + CLKDEV_INIT(NULL, "usbd", &clk_usbd), + CLKDEV_INIT(NULL, "spi", &clk_spi), + CLKDEV_INIT("bcm63xx_enet.0", "enet", &clk_enet_misc), +}; + +static struct clk_lookup bcm6345_clks[] = { + /* fixed rate clocks */ + CLKDEV_INIT(NULL, "periph", &clk_periph), + CLKDEV_INIT("bcm63xx_uart.0", "refclk", &clk_periph), + /* gated clocks */ + CLKDEV_INIT(NULL, "enet0", &clk_enet0), + CLKDEV_INIT(NULL, "enet1", &clk_enet1), + CLKDEV_INIT(NULL, "ephy", &clk_ephy), + CLKDEV_INIT(NULL, "usbh", &clk_usbh), + CLKDEV_INIT(NULL, "usbd", &clk_usbd), + CLKDEV_INIT(NULL, "spi", &clk_spi), + CLKDEV_INIT("bcm63xx_enet.0", "enet", &clk_enet_misc), +}; + +static struct clk_lookup bcm6348_clks[] = { + /* fixed rate clocks */ + CLKDEV_INIT(NULL, "periph", &clk_periph), + CLKDEV_INIT("bcm63xx_uart.0", "refclk", &clk_periph), + /* gated clocks */ + CLKDEV_INIT(NULL, "enet0", &clk_enet0), + CLKDEV_INIT(NULL, "enet1", &clk_enet1), + CLKDEV_INIT(NULL, "ephy", &clk_ephy), + CLKDEV_INIT(NULL, "usbh", &clk_usbh), + CLKDEV_INIT(NULL, "usbd", &clk_usbd), + CLKDEV_INIT(NULL, "spi", &clk_spi), + CLKDEV_INIT("bcm63xx_enet.0", "enet", &clk_enet_misc), + CLKDEV_INIT("bcm63xx_enet.1", "enet", &clk_enet_misc), +}; + +static struct clk_lookup bcm6358_clks[] = { + /* fixed rate clocks */ + CLKDEV_INIT(NULL, "periph", &clk_periph), + CLKDEV_INIT("bcm63xx_uart.0", "refclk", &clk_periph), + CLKDEV_INIT("bcm63xx_uart.1", "refclk", &clk_periph), + /* gated clocks */ + CLKDEV_INIT(NULL, "enet0", &clk_enet0), + CLKDEV_INIT(NULL, "enet1", &clk_enet1), + CLKDEV_INIT(NULL, "ephy", &clk_ephy), + CLKDEV_INIT(NULL, "usbh", &clk_usbh), + CLKDEV_INIT(NULL, "usbd", &clk_usbd), + CLKDEV_INIT(NULL, "spi", &clk_spi), + CLKDEV_INIT(NULL, "pcm", &clk_pcm), + CLKDEV_INIT(NULL, "swpkt_sar", &clk_swpkt_sar), + CLKDEV_INIT(NULL, "swpkt_usb", &clk_swpkt_usb), + CLKDEV_INIT("bcm63xx_enet.0", "enet", &clk_enet0), + CLKDEV_INIT("bcm63xx_enet.1", "enet", &clk_enet1), +}; + +static struct clk_lookup bcm6362_clks[] = { + /* fixed rate clocks */ + CLKDEV_INIT(NULL, "periph", &clk_periph), + CLKDEV_INIT("bcm63xx_uart.0", "refclk", &clk_periph), + CLKDEV_INIT("bcm63xx_uart.1", "refclk", &clk_periph), + CLKDEV_INIT("bcm63xx-hsspi.0", "pll", &clk_hsspi_pll), + /* gated clocks */ + CLKDEV_INIT(NULL, "enetsw", &clk_enetsw), + CLKDEV_INIT(NULL, "usbh", &clk_usbh), + CLKDEV_INIT(NULL, "usbd", &clk_usbd), + CLKDEV_INIT(NULL, "spi", &clk_spi), + CLKDEV_INIT(NULL, "hsspi", &clk_hsspi), + CLKDEV_INIT(NULL, "pcie", &clk_pcie), + CLKDEV_INIT(NULL, "ipsec", &clk_ipsec), +}; + +static struct clk_lookup bcm6368_clks[] = { + /* fixed rate clocks */ + CLKDEV_INIT(NULL, "periph", &clk_periph), + CLKDEV_INIT("bcm63xx_uart.0", "refclk", &clk_periph), + CLKDEV_INIT("bcm63xx_uart.1", "refclk", &clk_periph), + /* gated clocks */ + CLKDEV_INIT(NULL, "enetsw", &clk_enetsw), + CLKDEV_INIT(NULL, "usbh", &clk_usbh), + CLKDEV_INIT(NULL, "usbd", &clk_usbd), + CLKDEV_INIT(NULL, "spi", &clk_spi), + CLKDEV_INIT(NULL, "xtm", &clk_xtm), + CLKDEV_INIT(NULL, "ipsec", &clk_ipsec), +}; + +#define HSSPI_PLL_HZ_6328 133333333 +#define HSSPI_PLL_HZ_6362 400000000 + +static int __init bcm63xx_clk_init(void) +{ + switch (bcm63xx_get_cpu_id()) { + case BCM3368_CPU_ID: + clkdev_add_table(bcm3368_clks, ARRAY_SIZE(bcm3368_clks)); + break; + case BCM6328_CPU_ID: + clk_hsspi_pll.rate = HSSPI_PLL_HZ_6328; + clkdev_add_table(bcm6328_clks, ARRAY_SIZE(bcm6328_clks)); + break; + case BCM6338_CPU_ID: + clkdev_add_table(bcm6338_clks, ARRAY_SIZE(bcm6338_clks)); + break; + case BCM6345_CPU_ID: + clkdev_add_table(bcm6345_clks, ARRAY_SIZE(bcm6345_clks)); + break; + case BCM6348_CPU_ID: + clkdev_add_table(bcm6348_clks, ARRAY_SIZE(bcm6348_clks)); + break; + case BCM6358_CPU_ID: + clkdev_add_table(bcm6358_clks, ARRAY_SIZE(bcm6358_clks)); + break; + case BCM6362_CPU_ID: + clk_hsspi_pll.rate = HSSPI_PLL_HZ_6362; + clkdev_add_table(bcm6362_clks, ARRAY_SIZE(bcm6362_clks)); + break; + case BCM6368_CPU_ID: + clkdev_add_table(bcm6368_clks, ARRAY_SIZE(bcm6368_clks)); + break; + } + + return 0; +} +arch_initcall(bcm63xx_clk_init); diff --git a/arch/mips/bcm63xx/cpu.c b/arch/mips/bcm63xx/cpu.c new file mode 100644 index 000000000..8e3e199dd --- /dev/null +++ b/arch/mips/bcm63xx/cpu.c @@ -0,0 +1,385 @@ +/* + * 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) 2009 Florian Fainelli <florian@openwrt.org> + */ + +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/cpu.h> +#include <asm/cpu.h> +#include <asm/cpu-info.h> +#include <asm/mipsregs.h> +#include <bcm63xx_cpu.h> +#include <bcm63xx_regs.h> +#include <bcm63xx_io.h> +#include <bcm63xx_irq.h> + +const unsigned long *bcm63xx_regs_base; +EXPORT_SYMBOL(bcm63xx_regs_base); + +const int *bcm63xx_irqs; +EXPORT_SYMBOL(bcm63xx_irqs); + +u16 bcm63xx_cpu_id __read_mostly; +EXPORT_SYMBOL(bcm63xx_cpu_id); + +static u8 bcm63xx_cpu_rev; +static unsigned int bcm63xx_cpu_freq; +static unsigned int bcm63xx_memory_size; + +static const unsigned long bcm3368_regs_base[] = { + __GEN_CPU_REGS_TABLE(3368) +}; + +static const int bcm3368_irqs[] = { + __GEN_CPU_IRQ_TABLE(3368) +}; + +static const unsigned long bcm6328_regs_base[] = { + __GEN_CPU_REGS_TABLE(6328) +}; + +static const int bcm6328_irqs[] = { + __GEN_CPU_IRQ_TABLE(6328) +}; + +static const unsigned long bcm6338_regs_base[] = { + __GEN_CPU_REGS_TABLE(6338) +}; + +static const int bcm6338_irqs[] = { + __GEN_CPU_IRQ_TABLE(6338) +}; + +static const unsigned long bcm6345_regs_base[] = { + __GEN_CPU_REGS_TABLE(6345) +}; + +static const int bcm6345_irqs[] = { + __GEN_CPU_IRQ_TABLE(6345) +}; + +static const unsigned long bcm6348_regs_base[] = { + __GEN_CPU_REGS_TABLE(6348) +}; + +static const int bcm6348_irqs[] = { + __GEN_CPU_IRQ_TABLE(6348) + +}; + +static const unsigned long bcm6358_regs_base[] = { + __GEN_CPU_REGS_TABLE(6358) +}; + +static const int bcm6358_irqs[] = { + __GEN_CPU_IRQ_TABLE(6358) + +}; + +static const unsigned long bcm6362_regs_base[] = { + __GEN_CPU_REGS_TABLE(6362) +}; + +static const int bcm6362_irqs[] = { + __GEN_CPU_IRQ_TABLE(6362) + +}; + +static const unsigned long bcm6368_regs_base[] = { + __GEN_CPU_REGS_TABLE(6368) +}; + +static const int bcm6368_irqs[] = { + __GEN_CPU_IRQ_TABLE(6368) + +}; + +u8 bcm63xx_get_cpu_rev(void) +{ + return bcm63xx_cpu_rev; +} + +EXPORT_SYMBOL(bcm63xx_get_cpu_rev); + +unsigned int bcm63xx_get_cpu_freq(void) +{ + return bcm63xx_cpu_freq; +} + +unsigned int bcm63xx_get_memory_size(void) +{ + return bcm63xx_memory_size; +} + +static unsigned int detect_cpu_clock(void) +{ + u16 cpu_id = bcm63xx_get_cpu_id(); + + switch (cpu_id) { + case BCM3368_CPU_ID: + return 300000000; + + case BCM6328_CPU_ID: + { + unsigned int tmp, mips_pll_fcvo; + + tmp = bcm_misc_readl(MISC_STRAPBUS_6328_REG); + mips_pll_fcvo = (tmp & STRAPBUS_6328_FCVO_MASK) + >> STRAPBUS_6328_FCVO_SHIFT; + + switch (mips_pll_fcvo) { + case 0x12: + case 0x14: + case 0x19: + return 160000000; + case 0x1c: + return 192000000; + case 0x13: + case 0x15: + return 200000000; + case 0x1a: + return 384000000; + case 0x16: + return 400000000; + default: + return 320000000; + } + + } + case BCM6338_CPU_ID: + /* BCM6338 has a fixed 240 Mhz frequency */ + return 240000000; + + case BCM6345_CPU_ID: + /* BCM6345 has a fixed 140Mhz frequency */ + return 140000000; + + case BCM6348_CPU_ID: + { + unsigned int tmp, n1, n2, m1; + + /* 16MHz * (N1 + 1) * (N2 + 2) / (M1_CPU + 1) */ + tmp = bcm_perf_readl(PERF_MIPSPLLCTL_REG); + n1 = (tmp & MIPSPLLCTL_N1_MASK) >> MIPSPLLCTL_N1_SHIFT; + n2 = (tmp & MIPSPLLCTL_N2_MASK) >> MIPSPLLCTL_N2_SHIFT; + m1 = (tmp & MIPSPLLCTL_M1CPU_MASK) >> MIPSPLLCTL_M1CPU_SHIFT; + n1 += 1; + n2 += 2; + m1 += 1; + return (16 * 1000000 * n1 * n2) / m1; + } + + case BCM6358_CPU_ID: + { + unsigned int tmp, n1, n2, m1; + + /* 16MHz * N1 * N2 / M1_CPU */ + tmp = bcm_ddr_readl(DDR_DMIPSPLLCFG_REG); + n1 = (tmp & DMIPSPLLCFG_N1_MASK) >> DMIPSPLLCFG_N1_SHIFT; + n2 = (tmp & DMIPSPLLCFG_N2_MASK) >> DMIPSPLLCFG_N2_SHIFT; + m1 = (tmp & DMIPSPLLCFG_M1_MASK) >> DMIPSPLLCFG_M1_SHIFT; + return (16 * 1000000 * n1 * n2) / m1; + } + + case BCM6362_CPU_ID: + { + unsigned int tmp, mips_pll_fcvo; + + tmp = bcm_misc_readl(MISC_STRAPBUS_6362_REG); + mips_pll_fcvo = (tmp & STRAPBUS_6362_FCVO_MASK) + >> STRAPBUS_6362_FCVO_SHIFT; + switch (mips_pll_fcvo) { + case 0x03: + case 0x0b: + case 0x13: + case 0x1b: + return 240000000; + case 0x04: + case 0x0c: + case 0x14: + case 0x1c: + return 160000000; + case 0x05: + case 0x0e: + case 0x16: + case 0x1e: + case 0x1f: + return 400000000; + case 0x06: + return 440000000; + case 0x07: + case 0x17: + return 384000000; + case 0x15: + case 0x1d: + return 200000000; + default: + return 320000000; + } + } + case BCM6368_CPU_ID: + { + unsigned int tmp, p1, p2, ndiv, m1; + + /* (64MHz / P1) * P2 * NDIV / M1_CPU */ + tmp = bcm_ddr_readl(DDR_DMIPSPLLCFG_6368_REG); + + p1 = (tmp & DMIPSPLLCFG_6368_P1_MASK) >> + DMIPSPLLCFG_6368_P1_SHIFT; + + p2 = (tmp & DMIPSPLLCFG_6368_P2_MASK) >> + DMIPSPLLCFG_6368_P2_SHIFT; + + ndiv = (tmp & DMIPSPLLCFG_6368_NDIV_MASK) >> + DMIPSPLLCFG_6368_NDIV_SHIFT; + + tmp = bcm_ddr_readl(DDR_DMIPSPLLDIV_6368_REG); + m1 = (tmp & DMIPSPLLDIV_6368_MDIV_MASK) >> + DMIPSPLLDIV_6368_MDIV_SHIFT; + + return (((64 * 1000000) / p1) * p2 * ndiv) / m1; + } + + default: + panic("Failed to detect clock for CPU with id=%04X\n", cpu_id); + } +} + +/* + * attempt to detect the amount of memory installed + */ +static unsigned int detect_memory_size(void) +{ + unsigned int cols = 0, rows = 0, is_32bits = 0, banks = 0; + u32 val; + + if (BCMCPU_IS_6328() || BCMCPU_IS_6362()) + return bcm_ddr_readl(DDR_CSEND_REG) << 24; + + if (BCMCPU_IS_6345()) { + val = bcm_sdram_readl(SDRAM_MBASE_REG); + return val * 8 * 1024 * 1024; + } + + if (BCMCPU_IS_6338() || BCMCPU_IS_6348()) { + val = bcm_sdram_readl(SDRAM_CFG_REG); + rows = (val & SDRAM_CFG_ROW_MASK) >> SDRAM_CFG_ROW_SHIFT; + cols = (val & SDRAM_CFG_COL_MASK) >> SDRAM_CFG_COL_SHIFT; + is_32bits = (val & SDRAM_CFG_32B_MASK) ? 1 : 0; + banks = (val & SDRAM_CFG_BANK_MASK) ? 2 : 1; + } + + if (BCMCPU_IS_3368() || BCMCPU_IS_6358() || BCMCPU_IS_6368()) { + val = bcm_memc_readl(MEMC_CFG_REG); + rows = (val & MEMC_CFG_ROW_MASK) >> MEMC_CFG_ROW_SHIFT; + cols = (val & MEMC_CFG_COL_MASK) >> MEMC_CFG_COL_SHIFT; + is_32bits = (val & MEMC_CFG_32B_MASK) ? 0 : 1; + banks = 2; + } + + /* 0 => 11 address bits ... 2 => 13 address bits */ + rows += 11; + + /* 0 => 8 address bits ... 2 => 10 address bits */ + cols += 8; + + return 1 << (cols + rows + (is_32bits + 1) + banks); +} + +void __init bcm63xx_cpu_init(void) +{ + unsigned int tmp; + unsigned int cpu = smp_processor_id(); + u32 chipid_reg; + + /* soc registers location depends on cpu type */ + chipid_reg = 0; + + switch (current_cpu_type()) { + case CPU_BMIPS3300: + if ((read_c0_prid() & PRID_IMP_MASK) != PRID_IMP_BMIPS3300_ALT) + __cpu_name[cpu] = "Broadcom BCM6338"; + fallthrough; + case CPU_BMIPS32: + chipid_reg = BCM_6345_PERF_BASE; + break; + case CPU_BMIPS4350: + switch ((read_c0_prid() & PRID_REV_MASK)) { + case 0x04: + chipid_reg = BCM_3368_PERF_BASE; + break; + case 0x10: + chipid_reg = BCM_6345_PERF_BASE; + break; + default: + chipid_reg = BCM_6368_PERF_BASE; + break; + } + break; + } + + /* + * really early to panic, but delaying panic would not help since we + * will never get any working console + */ + if (!chipid_reg) + panic("unsupported Broadcom CPU"); + + /* read out CPU type */ + tmp = bcm_readl(chipid_reg); + bcm63xx_cpu_id = (tmp & REV_CHIPID_MASK) >> REV_CHIPID_SHIFT; + bcm63xx_cpu_rev = (tmp & REV_REVID_MASK) >> REV_REVID_SHIFT; + + switch (bcm63xx_cpu_id) { + case BCM3368_CPU_ID: + bcm63xx_regs_base = bcm3368_regs_base; + bcm63xx_irqs = bcm3368_irqs; + break; + case BCM6328_CPU_ID: + bcm63xx_regs_base = bcm6328_regs_base; + bcm63xx_irqs = bcm6328_irqs; + break; + case BCM6338_CPU_ID: + bcm63xx_regs_base = bcm6338_regs_base; + bcm63xx_irqs = bcm6338_irqs; + break; + case BCM6345_CPU_ID: + bcm63xx_regs_base = bcm6345_regs_base; + bcm63xx_irqs = bcm6345_irqs; + break; + case BCM6348_CPU_ID: + bcm63xx_regs_base = bcm6348_regs_base; + bcm63xx_irqs = bcm6348_irqs; + break; + case BCM6358_CPU_ID: + bcm63xx_regs_base = bcm6358_regs_base; + bcm63xx_irqs = bcm6358_irqs; + break; + case BCM6362_CPU_ID: + bcm63xx_regs_base = bcm6362_regs_base; + bcm63xx_irqs = bcm6362_irqs; + break; + case BCM6368_CPU_ID: + bcm63xx_regs_base = bcm6368_regs_base; + bcm63xx_irqs = bcm6368_irqs; + break; + default: + panic("unsupported broadcom CPU %x", bcm63xx_cpu_id); + break; + } + + bcm63xx_cpu_freq = detect_cpu_clock(); + bcm63xx_memory_size = detect_memory_size(); + + pr_info("Detected Broadcom 0x%04x CPU revision %02x\n", + bcm63xx_cpu_id, bcm63xx_cpu_rev); + pr_info("CPU frequency is %u MHz\n", + bcm63xx_cpu_freq / 1000000); + pr_info("%uMB of RAM installed\n", + bcm63xx_memory_size >> 20); +} diff --git a/arch/mips/bcm63xx/cs.c b/arch/mips/bcm63xx/cs.c new file mode 100644 index 000000000..29205badc --- /dev/null +++ b/arch/mips/bcm63xx/cs.c @@ -0,0 +1,145 @@ +/* + * 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> + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/spinlock.h> +#include <linux/log2.h> +#include <bcm63xx_cpu.h> +#include <bcm63xx_io.h> +#include <bcm63xx_regs.h> +#include <bcm63xx_cs.h> + +static DEFINE_SPINLOCK(bcm63xx_cs_lock); + +/* + * check if given chip select exists + */ +static int is_valid_cs(unsigned int cs) +{ + if (cs > 6) + return 0; + return 1; +} + +/* + * Configure chipselect base address and size (bytes). + * Size must be a power of two between 8k and 256M. + */ +int bcm63xx_set_cs_base(unsigned int cs, u32 base, unsigned int size) +{ + unsigned long flags; + u32 val; + + if (!is_valid_cs(cs)) + return -EINVAL; + + /* sanity check on size */ + if (size != roundup_pow_of_two(size)) + return -EINVAL; + + if (size < 8 * 1024 || size > 256 * 1024 * 1024) + return -EINVAL; + + val = (base & MPI_CSBASE_BASE_MASK); + /* 8k => 0 - 256M => 15 */ + val |= (ilog2(size) - ilog2(8 * 1024)) << MPI_CSBASE_SIZE_SHIFT; + + spin_lock_irqsave(&bcm63xx_cs_lock, flags); + bcm_mpi_writel(val, MPI_CSBASE_REG(cs)); + spin_unlock_irqrestore(&bcm63xx_cs_lock, flags); + + return 0; +} + +EXPORT_SYMBOL(bcm63xx_set_cs_base); + +/* + * configure chipselect timing (ns) + */ +int bcm63xx_set_cs_timing(unsigned int cs, unsigned int wait, + unsigned int setup, unsigned int hold) +{ + unsigned long flags; + u32 val; + + if (!is_valid_cs(cs)) + return -EINVAL; + + spin_lock_irqsave(&bcm63xx_cs_lock, flags); + val = bcm_mpi_readl(MPI_CSCTL_REG(cs)); + val &= ~(MPI_CSCTL_WAIT_MASK); + val &= ~(MPI_CSCTL_SETUP_MASK); + val &= ~(MPI_CSCTL_HOLD_MASK); + val |= wait << MPI_CSCTL_WAIT_SHIFT; + val |= setup << MPI_CSCTL_SETUP_SHIFT; + val |= hold << MPI_CSCTL_HOLD_SHIFT; + bcm_mpi_writel(val, MPI_CSCTL_REG(cs)); + spin_unlock_irqrestore(&bcm63xx_cs_lock, flags); + + return 0; +} + +EXPORT_SYMBOL(bcm63xx_set_cs_timing); + +/* + * configure other chipselect parameter (data bus size, ...) + */ +int bcm63xx_set_cs_param(unsigned int cs, u32 params) +{ + unsigned long flags; + u32 val; + + if (!is_valid_cs(cs)) + return -EINVAL; + + /* none of this fields apply to pcmcia */ + if (cs == MPI_CS_PCMCIA_COMMON || + cs == MPI_CS_PCMCIA_ATTR || + cs == MPI_CS_PCMCIA_IO) + return -EINVAL; + + spin_lock_irqsave(&bcm63xx_cs_lock, flags); + val = bcm_mpi_readl(MPI_CSCTL_REG(cs)); + val &= ~(MPI_CSCTL_DATA16_MASK); + val &= ~(MPI_CSCTL_SYNCMODE_MASK); + val &= ~(MPI_CSCTL_TSIZE_MASK); + val &= ~(MPI_CSCTL_ENDIANSWAP_MASK); + val |= params; + bcm_mpi_writel(val, MPI_CSCTL_REG(cs)); + spin_unlock_irqrestore(&bcm63xx_cs_lock, flags); + + return 0; +} + +EXPORT_SYMBOL(bcm63xx_set_cs_param); + +/* + * set cs status (enable/disable) + */ +int bcm63xx_set_cs_status(unsigned int cs, int enable) +{ + unsigned long flags; + u32 val; + + if (!is_valid_cs(cs)) + return -EINVAL; + + spin_lock_irqsave(&bcm63xx_cs_lock, flags); + val = bcm_mpi_readl(MPI_CSCTL_REG(cs)); + if (enable) + val |= MPI_CSCTL_ENABLE_MASK; + else + val &= ~MPI_CSCTL_ENABLE_MASK; + bcm_mpi_writel(val, MPI_CSCTL_REG(cs)); + spin_unlock_irqrestore(&bcm63xx_cs_lock, flags); + return 0; +} + +EXPORT_SYMBOL(bcm63xx_set_cs_status); diff --git a/arch/mips/bcm63xx/dev-enet.c b/arch/mips/bcm63xx/dev-enet.c new file mode 100644 index 000000000..8e73d65f3 --- /dev/null +++ b/arch/mips/bcm63xx/dev-enet.c @@ -0,0 +1,327 @@ +/* + * 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> + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/export.h> +#include <bcm63xx_dev_enet.h> +#include <bcm63xx_io.h> +#include <bcm63xx_regs.h> + +static const unsigned long bcm6348_regs_enetdmac[] = { + [ENETDMAC_CHANCFG] = ENETDMAC_CHANCFG_REG, + [ENETDMAC_IR] = ENETDMAC_IR_REG, + [ENETDMAC_IRMASK] = ENETDMAC_IRMASK_REG, + [ENETDMAC_MAXBURST] = ENETDMAC_MAXBURST_REG, +}; + +static const unsigned long bcm6345_regs_enetdmac[] = { + [ENETDMAC_CHANCFG] = ENETDMA_6345_CHANCFG_REG, + [ENETDMAC_IR] = ENETDMA_6345_IR_REG, + [ENETDMAC_IRMASK] = ENETDMA_6345_IRMASK_REG, + [ENETDMAC_MAXBURST] = ENETDMA_6345_MAXBURST_REG, + [ENETDMAC_BUFALLOC] = ENETDMA_6345_BUFALLOC_REG, + [ENETDMAC_RSTART] = ENETDMA_6345_RSTART_REG, + [ENETDMAC_FC] = ENETDMA_6345_FC_REG, + [ENETDMAC_LEN] = ENETDMA_6345_LEN_REG, +}; + +const unsigned long *bcm63xx_regs_enetdmac; +EXPORT_SYMBOL(bcm63xx_regs_enetdmac); + +static __init void bcm63xx_enetdmac_regs_init(void) +{ + if (BCMCPU_IS_6345()) + bcm63xx_regs_enetdmac = bcm6345_regs_enetdmac; + else + bcm63xx_regs_enetdmac = bcm6348_regs_enetdmac; +} + +static struct resource shared_res[] = { + { + .start = -1, /* filled at runtime */ + .end = -1, /* filled at runtime */ + .flags = IORESOURCE_MEM, + }, + { + .start = -1, /* filled at runtime */ + .end = -1, /* filled at runtime */ + .flags = IORESOURCE_MEM, + }, + { + .start = -1, /* filled at runtime */ + .end = -1, /* filled at runtime */ + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device bcm63xx_enet_shared_device = { + .name = "bcm63xx_enet_shared", + .id = 0, + .num_resources = ARRAY_SIZE(shared_res), + .resource = shared_res, +}; + +static int shared_device_registered; + +static u64 enet_dmamask = DMA_BIT_MASK(32); + +static struct resource enet0_res[] = { + { + .start = -1, /* filled at runtime */ + .end = -1, /* filled at runtime */ + .flags = IORESOURCE_MEM, + }, + { + .start = -1, /* filled at runtime */ + .flags = IORESOURCE_IRQ, + }, + { + .start = -1, /* filled at runtime */ + .flags = IORESOURCE_IRQ, + }, + { + .start = -1, /* filled at runtime */ + .flags = IORESOURCE_IRQ, + }, +}; + +static struct bcm63xx_enet_platform_data enet0_pd; + +static struct platform_device bcm63xx_enet0_device = { + .name = "bcm63xx_enet", + .id = 0, + .num_resources = ARRAY_SIZE(enet0_res), + .resource = enet0_res, + .dev = { + .platform_data = &enet0_pd, + .dma_mask = &enet_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource enet1_res[] = { + { + .start = -1, /* filled at runtime */ + .end = -1, /* filled at runtime */ + .flags = IORESOURCE_MEM, + }, + { + .start = -1, /* filled at runtime */ + .flags = IORESOURCE_IRQ, + }, + { + .start = -1, /* filled at runtime */ + .flags = IORESOURCE_IRQ, + }, + { + .start = -1, /* filled at runtime */ + .flags = IORESOURCE_IRQ, + }, +}; + +static struct bcm63xx_enet_platform_data enet1_pd; + +static struct platform_device bcm63xx_enet1_device = { + .name = "bcm63xx_enet", + .id = 1, + .num_resources = ARRAY_SIZE(enet1_res), + .resource = enet1_res, + .dev = { + .platform_data = &enet1_pd, + .dma_mask = &enet_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource enetsw_res[] = { + { + /* start & end filled at runtime */ + .flags = IORESOURCE_MEM, + }, + { + /* start filled at runtime */ + .flags = IORESOURCE_IRQ, + }, + { + /* start filled at runtime */ + .flags = IORESOURCE_IRQ, + }, +}; + +static struct bcm63xx_enetsw_platform_data enetsw_pd; + +static struct platform_device bcm63xx_enetsw_device = { + .name = "bcm63xx_enetsw", + .num_resources = ARRAY_SIZE(enetsw_res), + .resource = enetsw_res, + .dev = { + .platform_data = &enetsw_pd, + .dma_mask = &enet_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static int __init register_shared(void) +{ + int ret, chan_count; + + if (shared_device_registered) + return 0; + + bcm63xx_enetdmac_regs_init(); + + shared_res[0].start = bcm63xx_regset_address(RSET_ENETDMA); + shared_res[0].end = shared_res[0].start; + if (BCMCPU_IS_6345()) + shared_res[0].end += (RSET_6345_ENETDMA_SIZE) - 1; + else + shared_res[0].end += (RSET_ENETDMA_SIZE) - 1; + + if (BCMCPU_IS_6328() || BCMCPU_IS_6362() || BCMCPU_IS_6368()) + chan_count = 32; + else if (BCMCPU_IS_6345()) + chan_count = 8; + else + chan_count = 16; + + shared_res[1].start = bcm63xx_regset_address(RSET_ENETDMAC); + shared_res[1].end = shared_res[1].start; + shared_res[1].end += RSET_ENETDMAC_SIZE(chan_count) - 1; + + shared_res[2].start = bcm63xx_regset_address(RSET_ENETDMAS); + shared_res[2].end = shared_res[2].start; + shared_res[2].end += RSET_ENETDMAS_SIZE(chan_count) - 1; + + ret = platform_device_register(&bcm63xx_enet_shared_device); + if (ret) + return ret; + shared_device_registered = 1; + + return 0; +} + +int __init bcm63xx_enet_register(int unit, + const struct bcm63xx_enet_platform_data *pd) +{ + struct platform_device *pdev; + struct bcm63xx_enet_platform_data *dpd; + int ret; + + if (unit > 1) + return -ENODEV; + + if (unit == 1 && (BCMCPU_IS_6338() || BCMCPU_IS_6345())) + return -ENODEV; + + ret = register_shared(); + if (ret) + return ret; + + if (unit == 0) { + enet0_res[0].start = bcm63xx_regset_address(RSET_ENET0); + enet0_res[0].end = enet0_res[0].start; + enet0_res[0].end += RSET_ENET_SIZE - 1; + enet0_res[1].start = bcm63xx_get_irq_number(IRQ_ENET0); + enet0_res[2].start = bcm63xx_get_irq_number(IRQ_ENET0_RXDMA); + enet0_res[3].start = bcm63xx_get_irq_number(IRQ_ENET0_TXDMA); + pdev = &bcm63xx_enet0_device; + } else { + enet1_res[0].start = bcm63xx_regset_address(RSET_ENET1); + enet1_res[0].end = enet1_res[0].start; + enet1_res[0].end += RSET_ENET_SIZE - 1; + enet1_res[1].start = bcm63xx_get_irq_number(IRQ_ENET1); + enet1_res[2].start = bcm63xx_get_irq_number(IRQ_ENET1_RXDMA); + enet1_res[3].start = bcm63xx_get_irq_number(IRQ_ENET1_TXDMA); + pdev = &bcm63xx_enet1_device; + } + + /* copy given platform data */ + dpd = pdev->dev.platform_data; + memcpy(dpd, pd, sizeof(*pd)); + + /* adjust them in case internal phy is used */ + if (dpd->use_internal_phy) { + + /* internal phy only exists for enet0 */ + if (unit == 1) + return -ENODEV; + + dpd->phy_id = 1; + dpd->has_phy_interrupt = 1; + dpd->phy_interrupt = bcm63xx_get_irq_number(IRQ_ENET_PHY); + } + + dpd->dma_chan_en_mask = ENETDMAC_CHANCFG_EN_MASK; + dpd->dma_chan_int_mask = ENETDMAC_IR_PKTDONE_MASK; + if (BCMCPU_IS_6345()) { + dpd->dma_chan_en_mask |= ENETDMAC_CHANCFG_CHAINING_MASK; + dpd->dma_chan_en_mask |= ENETDMAC_CHANCFG_WRAP_EN_MASK; + dpd->dma_chan_en_mask |= ENETDMAC_CHANCFG_FLOWC_EN_MASK; + dpd->dma_chan_int_mask |= ENETDMA_IR_BUFDONE_MASK; + dpd->dma_chan_int_mask |= ENETDMA_IR_NOTOWNER_MASK; + dpd->dma_chan_width = ENETDMA_6345_CHAN_WIDTH; + dpd->dma_desc_shift = ENETDMA_6345_DESC_SHIFT; + } else { + dpd->dma_has_sram = true; + dpd->dma_chan_width = ENETDMA_CHAN_WIDTH; + } + + if (unit == 0) { + dpd->rx_chan = 0; + dpd->tx_chan = 1; + } else { + dpd->rx_chan = 2; + dpd->tx_chan = 3; + } + + ret = platform_device_register(pdev); + if (ret) + return ret; + return 0; +} + +int __init +bcm63xx_enetsw_register(const struct bcm63xx_enetsw_platform_data *pd) +{ + int ret; + + if (!BCMCPU_IS_6328() && !BCMCPU_IS_6362() && !BCMCPU_IS_6368()) + return -ENODEV; + + ret = register_shared(); + if (ret) + return ret; + + enetsw_res[0].start = bcm63xx_regset_address(RSET_ENETSW); + enetsw_res[0].end = enetsw_res[0].start; + enetsw_res[0].end += RSET_ENETSW_SIZE - 1; + enetsw_res[1].start = bcm63xx_get_irq_number(IRQ_ENETSW_RXDMA0); + enetsw_res[2].start = bcm63xx_get_irq_number(IRQ_ENETSW_TXDMA0); + if (!enetsw_res[2].start) + enetsw_res[2].start = -1; + + memcpy(bcm63xx_enetsw_device.dev.platform_data, pd, sizeof(*pd)); + + if (BCMCPU_IS_6328()) + enetsw_pd.num_ports = ENETSW_PORTS_6328; + else if (BCMCPU_IS_6362() || BCMCPU_IS_6368()) + enetsw_pd.num_ports = ENETSW_PORTS_6368; + + enetsw_pd.dma_has_sram = true; + enetsw_pd.dma_chan_width = ENETDMA_CHAN_WIDTH; + enetsw_pd.dma_chan_en_mask = ENETDMAC_CHANCFG_EN_MASK; + enetsw_pd.dma_chan_int_mask = ENETDMAC_IR_PKTDONE_MASK; + + ret = platform_device_register(&bcm63xx_enetsw_device); + if (ret) + return ret; + + return 0; +} diff --git a/arch/mips/bcm63xx/dev-flash.c b/arch/mips/bcm63xx/dev-flash.c new file mode 100644 index 000000000..f9cc015d3 --- /dev/null +++ b/arch/mips/bcm63xx/dev-flash.c @@ -0,0 +1,131 @@ +/* + * Broadcom BCM63xx flash registration + * + * 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> + * Copyright (C) 2012 Jonas Gorski <jonas.gorski@gmail.com> + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> + +#include <bcm63xx_cpu.h> +#include <bcm63xx_dev_flash.h> +#include <bcm63xx_regs.h> +#include <bcm63xx_io.h> + +static struct mtd_partition mtd_partitions[] = { + { + .name = "cfe", + .offset = 0x0, + .size = 0x40000, + } +}; + +static const char *bcm63xx_part_types[] = { "bcm63xxpart", NULL }; + +static struct physmap_flash_data flash_data = { + .width = 2, + .parts = mtd_partitions, + .part_probe_types = bcm63xx_part_types, +}; + +static struct resource mtd_resources[] = { + { + .start = 0, /* filled at runtime */ + .end = 0, /* filled at runtime */ + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device mtd_dev = { + .name = "physmap-flash", + .resource = mtd_resources, + .num_resources = ARRAY_SIZE(mtd_resources), + .dev = { + .platform_data = &flash_data, + }, +}; + +static int __init bcm63xx_detect_flash_type(void) +{ + u32 val; + + switch (bcm63xx_get_cpu_id()) { + case BCM6328_CPU_ID: + val = bcm_misc_readl(MISC_STRAPBUS_6328_REG); + if (val & STRAPBUS_6328_BOOT_SEL_SERIAL) + return BCM63XX_FLASH_TYPE_SERIAL; + else + return BCM63XX_FLASH_TYPE_NAND; + case BCM6338_CPU_ID: + case BCM6345_CPU_ID: + case BCM6348_CPU_ID: + /* no way to auto detect so assume parallel */ + return BCM63XX_FLASH_TYPE_PARALLEL; + case BCM3368_CPU_ID: + case BCM6358_CPU_ID: + val = bcm_gpio_readl(GPIO_STRAPBUS_REG); + if (val & STRAPBUS_6358_BOOT_SEL_PARALLEL) + return BCM63XX_FLASH_TYPE_PARALLEL; + else + return BCM63XX_FLASH_TYPE_SERIAL; + case BCM6362_CPU_ID: + val = bcm_misc_readl(MISC_STRAPBUS_6362_REG); + if (val & STRAPBUS_6362_BOOT_SEL_SERIAL) + return BCM63XX_FLASH_TYPE_SERIAL; + else + return BCM63XX_FLASH_TYPE_NAND; + case BCM6368_CPU_ID: + val = bcm_gpio_readl(GPIO_STRAPBUS_REG); + switch (val & STRAPBUS_6368_BOOT_SEL_MASK) { + case STRAPBUS_6368_BOOT_SEL_NAND: + return BCM63XX_FLASH_TYPE_NAND; + case STRAPBUS_6368_BOOT_SEL_SERIAL: + return BCM63XX_FLASH_TYPE_SERIAL; + case STRAPBUS_6368_BOOT_SEL_PARALLEL: + return BCM63XX_FLASH_TYPE_PARALLEL; + } + fallthrough; + default: + return -EINVAL; + } +} + +int __init bcm63xx_flash_register(void) +{ + int flash_type; + u32 val; + + flash_type = bcm63xx_detect_flash_type(); + + switch (flash_type) { + case BCM63XX_FLASH_TYPE_PARALLEL: + /* read base address of boot chip select (0) */ + val = bcm_mpi_readl(MPI_CSBASE_REG(0)); + val &= MPI_CSBASE_BASE_MASK; + + mtd_resources[0].start = val; + mtd_resources[0].end = 0x1FFFFFFF; + + return platform_device_register(&mtd_dev); + case BCM63XX_FLASH_TYPE_SERIAL: + pr_warn("unsupported serial flash detected\n"); + return -ENODEV; + case BCM63XX_FLASH_TYPE_NAND: + pr_warn("unsupported NAND flash detected\n"); + return -ENODEV; + default: + pr_err("flash detection failed for BCM%x: %d\n", + bcm63xx_get_cpu_id(), flash_type); + return -ENODEV; + } +} diff --git a/arch/mips/bcm63xx/dev-hsspi.c b/arch/mips/bcm63xx/dev-hsspi.c new file mode 100644 index 000000000..696abc48e --- /dev/null +++ b/arch/mips/bcm63xx/dev-hsspi.c @@ -0,0 +1,47 @@ +/* + * 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) 2012 Jonas Gorski <jonas.gorski@gmail.com> + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> + +#include <bcm63xx_cpu.h> +#include <bcm63xx_dev_hsspi.h> +#include <bcm63xx_regs.h> + +static struct resource spi_resources[] = { + { + .start = -1, /* filled at runtime */ + .end = -1, /* filled at runtime */ + .flags = IORESOURCE_MEM, + }, + { + .start = -1, /* filled at runtime */ + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device bcm63xx_hsspi_device = { + .name = "bcm63xx-hsspi", + .id = 0, + .num_resources = ARRAY_SIZE(spi_resources), + .resource = spi_resources, +}; + +int __init bcm63xx_hsspi_register(void) +{ + if (!BCMCPU_IS_6328() && !BCMCPU_IS_6362()) + return -ENODEV; + + spi_resources[0].start = bcm63xx_regset_address(RSET_HSSPI); + spi_resources[0].end = spi_resources[0].start; + spi_resources[0].end += RSET_HSSPI_SIZE - 1; + spi_resources[1].start = bcm63xx_get_irq_number(IRQ_HSSPI); + + return platform_device_register(&bcm63xx_hsspi_device); +} diff --git a/arch/mips/bcm63xx/dev-pcmcia.c b/arch/mips/bcm63xx/dev-pcmcia.c new file mode 100644 index 000000000..9496cd236 --- /dev/null +++ b/arch/mips/bcm63xx/dev-pcmcia.c @@ -0,0 +1,144 @@ +/* + * 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> + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <asm/bootinfo.h> +#include <linux/platform_device.h> +#include <bcm63xx_cs.h> +#include <bcm63xx_cpu.h> +#include <bcm63xx_dev_pcmcia.h> +#include <bcm63xx_io.h> +#include <bcm63xx_regs.h> + +static struct resource pcmcia_resources[] = { + /* pcmcia registers */ + { + /* start & end filled at runtime */ + .flags = IORESOURCE_MEM, + }, + + /* pcmcia memory zone resources */ + { + .start = BCM_PCMCIA_COMMON_BASE_PA, + .end = BCM_PCMCIA_COMMON_END_PA, + .flags = IORESOURCE_MEM, + }, + { + .start = BCM_PCMCIA_ATTR_BASE_PA, + .end = BCM_PCMCIA_ATTR_END_PA, + .flags = IORESOURCE_MEM, + }, + { + .start = BCM_PCMCIA_IO_BASE_PA, + .end = BCM_PCMCIA_IO_END_PA, + .flags = IORESOURCE_MEM, + }, + + /* PCMCIA irq */ + { + /* start filled at runtime */ + .flags = IORESOURCE_IRQ, + }, + + /* declare PCMCIA IO resource also */ + { + .start = BCM_PCMCIA_IO_BASE_PA, + .end = BCM_PCMCIA_IO_END_PA, + .flags = IORESOURCE_IO, + }, +}; + +static struct bcm63xx_pcmcia_platform_data pd; + +static struct platform_device bcm63xx_pcmcia_device = { + .name = "bcm63xx_pcmcia", + .id = 0, + .num_resources = ARRAY_SIZE(pcmcia_resources), + .resource = pcmcia_resources, + .dev = { + .platform_data = &pd, + }, +}; + +static int __init config_pcmcia_cs(unsigned int cs, + u32 base, unsigned int size) +{ + int ret; + + ret = bcm63xx_set_cs_status(cs, 0); + if (!ret) + ret = bcm63xx_set_cs_base(cs, base, size); + if (!ret) + ret = bcm63xx_set_cs_status(cs, 1); + return ret; +} + +static const struct { + unsigned int cs; + unsigned int base; + unsigned int size; +} pcmcia_cs[3] __initconst = { + { + .cs = MPI_CS_PCMCIA_COMMON, + .base = BCM_PCMCIA_COMMON_BASE_PA, + .size = BCM_PCMCIA_COMMON_SIZE + }, + { + .cs = MPI_CS_PCMCIA_ATTR, + .base = BCM_PCMCIA_ATTR_BASE_PA, + .size = BCM_PCMCIA_ATTR_SIZE + }, + { + .cs = MPI_CS_PCMCIA_IO, + .base = BCM_PCMCIA_IO_BASE_PA, + .size = BCM_PCMCIA_IO_SIZE + }, +}; + +int __init bcm63xx_pcmcia_register(void) +{ + int ret, i; + + if (!BCMCPU_IS_6348() && !BCMCPU_IS_6358()) + return 0; + + /* use correct pcmcia ready gpio depending on processor */ + switch (bcm63xx_get_cpu_id()) { + case BCM6348_CPU_ID: + pd.ready_gpio = 22; + break; + + case BCM6358_CPU_ID: + pd.ready_gpio = 18; + break; + + default: + return -ENODEV; + } + + pcmcia_resources[0].start = bcm63xx_regset_address(RSET_PCMCIA); + pcmcia_resources[0].end = pcmcia_resources[0].start + + RSET_PCMCIA_SIZE - 1; + pcmcia_resources[4].start = bcm63xx_get_irq_number(IRQ_PCMCIA); + + /* configure pcmcia chip selects */ + for (i = 0; i < 3; i++) { + ret = config_pcmcia_cs(pcmcia_cs[i].cs, + pcmcia_cs[i].base, + pcmcia_cs[i].size); + if (ret) + goto out_err; + } + + return platform_device_register(&bcm63xx_pcmcia_device); + +out_err: + pr_err("unable to set pcmcia chip select\n"); + return ret; +} diff --git a/arch/mips/bcm63xx/dev-rng.c b/arch/mips/bcm63xx/dev-rng.c new file mode 100644 index 000000000..d277b4dc6 --- /dev/null +++ b/arch/mips/bcm63xx/dev-rng.c @@ -0,0 +1,40 @@ +/* + * 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) 2011 Florian Fainelli <florian@openwrt.org> + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <bcm63xx_cpu.h> + +static struct resource rng_resources[] = { + { + .start = -1, /* filled at runtime */ + .end = -1, /* filled at runtime */ + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device bcm63xx_rng_device = { + .name = "bcm63xx-rng", + .id = -1, + .num_resources = ARRAY_SIZE(rng_resources), + .resource = rng_resources, +}; + +int __init bcm63xx_rng_register(void) +{ + if (!BCMCPU_IS_6368()) + return -ENODEV; + + rng_resources[0].start = bcm63xx_regset_address(RSET_RNG); + rng_resources[0].end = rng_resources[0].start; + rng_resources[0].end += RSET_RNG_SIZE - 1; + + return platform_device_register(&bcm63xx_rng_device); +} +arch_initcall(bcm63xx_rng_register); diff --git a/arch/mips/bcm63xx/dev-spi.c b/arch/mips/bcm63xx/dev-spi.c new file mode 100644 index 000000000..232385441 --- /dev/null +++ b/arch/mips/bcm63xx/dev-spi.c @@ -0,0 +1,60 @@ +/* + * 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) 2009-2011 Florian Fainelli <florian@openwrt.org> + * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com> + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/clk.h> + +#include <bcm63xx_cpu.h> +#include <bcm63xx_dev_spi.h> +#include <bcm63xx_regs.h> + +static struct resource spi_resources[] = { + { + .start = -1, /* filled at runtime */ + .end = -1, /* filled at runtime */ + .flags = IORESOURCE_MEM, + }, + { + .start = -1, /* filled at runtime */ + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device bcm63xx_spi_device = { + .id = -1, + .num_resources = ARRAY_SIZE(spi_resources), + .resource = spi_resources, +}; + +int __init bcm63xx_spi_register(void) +{ + if (BCMCPU_IS_6328() || BCMCPU_IS_6345()) + return -ENODEV; + + spi_resources[0].start = bcm63xx_regset_address(RSET_SPI); + spi_resources[0].end = spi_resources[0].start; + spi_resources[1].start = bcm63xx_get_irq_number(IRQ_SPI); + + if (BCMCPU_IS_6338() || BCMCPU_IS_6348()) { + bcm63xx_spi_device.name = "bcm6348-spi", + spi_resources[0].end += BCM_6348_RSET_SPI_SIZE - 1; + } + + if (BCMCPU_IS_3368() || BCMCPU_IS_6358() || BCMCPU_IS_6362() || + BCMCPU_IS_6368()) { + bcm63xx_spi_device.name = "bcm6358-spi", + spi_resources[0].end += BCM_6358_RSET_SPI_SIZE - 1; + } + + return platform_device_register(&bcm63xx_spi_device); +} diff --git a/arch/mips/bcm63xx/dev-uart.c b/arch/mips/bcm63xx/dev-uart.c new file mode 100644 index 000000000..3bc7f3bfc --- /dev/null +++ b/arch/mips/bcm63xx/dev-uart.c @@ -0,0 +1,76 @@ +/* + * 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> + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <bcm63xx_cpu.h> + +static struct resource uart0_resources[] = { + { + /* start & end filled at runtime */ + .flags = IORESOURCE_MEM, + }, + { + /* start filled at runtime */ + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource uart1_resources[] = { + { + /* start & end filled at runtime */ + .flags = IORESOURCE_MEM, + }, + { + /* start filled at runtime */ + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device bcm63xx_uart_devices[] = { + { + .name = "bcm63xx_uart", + .id = 0, + .num_resources = ARRAY_SIZE(uart0_resources), + .resource = uart0_resources, + }, + + { + .name = "bcm63xx_uart", + .id = 1, + .num_resources = ARRAY_SIZE(uart1_resources), + .resource = uart1_resources, + } +}; + +int __init bcm63xx_uart_register(unsigned int id) +{ + if (id >= ARRAY_SIZE(bcm63xx_uart_devices)) + return -ENODEV; + + if (id == 1 && (!BCMCPU_IS_3368() && !BCMCPU_IS_6358() && + !BCMCPU_IS_6368())) + return -ENODEV; + + if (id == 0) { + uart0_resources[0].start = bcm63xx_regset_address(RSET_UART0); + uart0_resources[0].end = uart0_resources[0].start + + RSET_UART_SIZE - 1; + uart0_resources[1].start = bcm63xx_get_irq_number(IRQ_UART0); + } + + if (id == 1) { + uart1_resources[0].start = bcm63xx_regset_address(RSET_UART1); + uart1_resources[0].end = uart1_resources[0].start + + RSET_UART_SIZE - 1; + uart1_resources[1].start = bcm63xx_get_irq_number(IRQ_UART1); + } + + return platform_device_register(&bcm63xx_uart_devices[id]); +} diff --git a/arch/mips/bcm63xx/dev-usb-usbd.c b/arch/mips/bcm63xx/dev-usb-usbd.c new file mode 100644 index 000000000..508bd9d8d --- /dev/null +++ b/arch/mips/bcm63xx/dev-usb-usbd.c @@ -0,0 +1,65 @@ +/* + * 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) 2012 Kevin Cernekee <cernekee@gmail.com> + * Copyright (C) 2012 Broadcom Corporation + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <bcm63xx_cpu.h> +#include <bcm63xx_dev_usb_usbd.h> + +#define NUM_MMIO 2 +#define NUM_IRQ 7 + +static struct resource usbd_resources[NUM_MMIO + NUM_IRQ]; + +static u64 usbd_dmamask = DMA_BIT_MASK(32); + +static struct platform_device bcm63xx_usbd_device = { + .name = "bcm63xx_udc", + .id = -1, + .num_resources = ARRAY_SIZE(usbd_resources), + .resource = usbd_resources, + .dev = { + .dma_mask = &usbd_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +int __init bcm63xx_usbd_register(const struct bcm63xx_usbd_platform_data *pd) +{ + const int irq_list[NUM_IRQ] = { IRQ_USBD, + IRQ_USBD_RXDMA0, IRQ_USBD_TXDMA0, + IRQ_USBD_RXDMA1, IRQ_USBD_TXDMA1, + IRQ_USBD_RXDMA2, IRQ_USBD_TXDMA2 }; + int i; + + if (!BCMCPU_IS_6328() && !BCMCPU_IS_6368()) + return 0; + + usbd_resources[0].start = bcm63xx_regset_address(RSET_USBD); + usbd_resources[0].end = usbd_resources[0].start + RSET_USBD_SIZE - 1; + usbd_resources[0].flags = IORESOURCE_MEM; + + usbd_resources[1].start = bcm63xx_regset_address(RSET_USBDMA); + usbd_resources[1].end = usbd_resources[1].start + RSET_USBDMA_SIZE - 1; + usbd_resources[1].flags = IORESOURCE_MEM; + + for (i = 0; i < NUM_IRQ; i++) { + struct resource *r = &usbd_resources[NUM_MMIO + i]; + + r->start = r->end = bcm63xx_get_irq_number(irq_list[i]); + r->flags = IORESOURCE_IRQ; + } + + platform_device_add_data(&bcm63xx_usbd_device, pd, sizeof(*pd)); + + return platform_device_register(&bcm63xx_usbd_device); +} diff --git a/arch/mips/bcm63xx/dev-wdt.c b/arch/mips/bcm63xx/dev-wdt.c new file mode 100644 index 000000000..42130914a --- /dev/null +++ b/arch/mips/bcm63xx/dev-wdt.c @@ -0,0 +1,45 @@ +/* + * 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 Florian Fainelli <florian@openwrt.org> + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/platform_data/bcm7038_wdt.h> +#include <bcm63xx_cpu.h> + +static struct resource wdt_resources[] = { + { + .start = -1, /* filled at runtime */ + .end = -1, /* filled at runtime */ + .flags = IORESOURCE_MEM, + }, +}; + +static struct bcm7038_wdt_platform_data bcm63xx_wdt_pdata = { + .clk_name = "periph", +}; + +static struct platform_device bcm63xx_wdt_device = { + .name = "bcm63xx-wdt", + .id = -1, + .num_resources = ARRAY_SIZE(wdt_resources), + .resource = wdt_resources, + .dev = { + .platform_data = &bcm63xx_wdt_pdata, + }, +}; + +int __init bcm63xx_wdt_register(void) +{ + wdt_resources[0].start = bcm63xx_regset_address(RSET_WDT); + wdt_resources[0].end = wdt_resources[0].start; + wdt_resources[0].end += RSET_WDT_SIZE - 1; + + return platform_device_register(&bcm63xx_wdt_device); +} +arch_initcall(bcm63xx_wdt_register); diff --git a/arch/mips/bcm63xx/early_printk.c b/arch/mips/bcm63xx/early_printk.c new file mode 100644 index 000000000..9e9ec27c2 --- /dev/null +++ b/arch/mips/bcm63xx/early_printk.c @@ -0,0 +1,30 @@ +/* + * 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> + */ + +#include <bcm63xx_io.h> +#include <linux/serial_bcm63xx.h> +#include <asm/setup.h> + +static void wait_xfered(void) +{ + unsigned int val; + + /* wait for any previous char to be transmitted */ + do { + val = bcm_uart0_readl(UART_IR_REG); + if (val & UART_IR_STAT(UART_IR_TXEMPTY)) + break; + } while (1); +} + +void prom_putchar(char c) +{ + wait_xfered(); + bcm_uart0_writel(c, UART_FIFO_REG); + wait_xfered(); +} diff --git a/arch/mips/bcm63xx/gpio.c b/arch/mips/bcm63xx/gpio.c new file mode 100644 index 000000000..5c4a233db --- /dev/null +++ b/arch/mips/bcm63xx/gpio.c @@ -0,0 +1,148 @@ +/* + * 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-2011 Florian Fainelli <florian@openwrt.org> + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/gpio/driver.h> + +#include <bcm63xx_cpu.h> +#include <bcm63xx_gpio.h> +#include <bcm63xx_io.h> +#include <bcm63xx_regs.h> + +static u32 gpio_out_low_reg; + +static void bcm63xx_gpio_out_low_reg_init(void) +{ + switch (bcm63xx_get_cpu_id()) { + case BCM6345_CPU_ID: + gpio_out_low_reg = GPIO_DATA_LO_REG_6345; + break; + default: + gpio_out_low_reg = GPIO_DATA_LO_REG; + break; + } +} + +static DEFINE_SPINLOCK(bcm63xx_gpio_lock); +static u32 gpio_out_low, gpio_out_high; + +static void bcm63xx_gpio_set(struct gpio_chip *chip, + unsigned gpio, int val) +{ + u32 reg; + u32 mask; + u32 *v; + unsigned long flags; + + BUG_ON(gpio >= chip->ngpio); + + if (gpio < 32) { + reg = gpio_out_low_reg; + mask = 1 << gpio; + v = &gpio_out_low; + } else { + reg = GPIO_DATA_HI_REG; + mask = 1 << (gpio - 32); + v = &gpio_out_high; + } + + spin_lock_irqsave(&bcm63xx_gpio_lock, flags); + if (val) + *v |= mask; + else + *v &= ~mask; + bcm_gpio_writel(*v, reg); + spin_unlock_irqrestore(&bcm63xx_gpio_lock, flags); +} + +static int bcm63xx_gpio_get(struct gpio_chip *chip, unsigned gpio) +{ + u32 reg; + u32 mask; + + BUG_ON(gpio >= chip->ngpio); + + if (gpio < 32) { + reg = gpio_out_low_reg; + mask = 1 << gpio; + } else { + reg = GPIO_DATA_HI_REG; + mask = 1 << (gpio - 32); + } + + return !!(bcm_gpio_readl(reg) & mask); +} + +static int bcm63xx_gpio_set_direction(struct gpio_chip *chip, + unsigned gpio, int dir) +{ + u32 reg; + u32 mask; + u32 tmp; + unsigned long flags; + + BUG_ON(gpio >= chip->ngpio); + + if (gpio < 32) { + reg = GPIO_CTL_LO_REG; + mask = 1 << gpio; + } else { + reg = GPIO_CTL_HI_REG; + mask = 1 << (gpio - 32); + } + + spin_lock_irqsave(&bcm63xx_gpio_lock, flags); + tmp = bcm_gpio_readl(reg); + if (dir == BCM63XX_GPIO_DIR_IN) + tmp &= ~mask; + else + tmp |= mask; + bcm_gpio_writel(tmp, reg); + spin_unlock_irqrestore(&bcm63xx_gpio_lock, flags); + + return 0; +} + +static int bcm63xx_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +{ + return bcm63xx_gpio_set_direction(chip, gpio, BCM63XX_GPIO_DIR_IN); +} + +static int bcm63xx_gpio_direction_output(struct gpio_chip *chip, + unsigned gpio, int value) +{ + bcm63xx_gpio_set(chip, gpio, value); + return bcm63xx_gpio_set_direction(chip, gpio, BCM63XX_GPIO_DIR_OUT); +} + + +static struct gpio_chip bcm63xx_gpio_chip = { + .label = "bcm63xx-gpio", + .direction_input = bcm63xx_gpio_direction_input, + .direction_output = bcm63xx_gpio_direction_output, + .get = bcm63xx_gpio_get, + .set = bcm63xx_gpio_set, + .base = 0, +}; + +int __init bcm63xx_gpio_init(void) +{ + bcm63xx_gpio_out_low_reg_init(); + + gpio_out_low = bcm_gpio_readl(gpio_out_low_reg); + if (!BCMCPU_IS_6345()) + gpio_out_high = bcm_gpio_readl(GPIO_DATA_HI_REG); + bcm63xx_gpio_chip.ngpio = bcm63xx_gpio_count(); + pr_info("registering %d GPIOs\n", bcm63xx_gpio_chip.ngpio); + + return gpiochip_add_data(&bcm63xx_gpio_chip, NULL); +} diff --git a/arch/mips/bcm63xx/irq.c b/arch/mips/bcm63xx/irq.c new file mode 100644 index 000000000..254801344 --- /dev/null +++ b/arch/mips/bcm63xx/irq.c @@ -0,0 +1,553 @@ +/* + * 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 Nicolas Schichan <nschichan@freebox.fr> + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/spinlock.h> +#include <asm/irq_cpu.h> +#include <asm/mipsregs.h> +#include <bcm63xx_cpu.h> +#include <bcm63xx_regs.h> +#include <bcm63xx_io.h> +#include <bcm63xx_irq.h> + + +static DEFINE_SPINLOCK(ipic_lock); +static DEFINE_SPINLOCK(epic_lock); + +static u32 irq_stat_addr[2]; +static u32 irq_mask_addr[2]; +static void (*dispatch_internal)(int cpu); +static int is_ext_irq_cascaded; +static unsigned int ext_irq_count; +static unsigned int ext_irq_start, ext_irq_end; +static unsigned int ext_irq_cfg_reg1, ext_irq_cfg_reg2; +static void (*internal_irq_mask)(struct irq_data *d); +static void (*internal_irq_unmask)(struct irq_data *d, const struct cpumask *m); + + +static inline u32 get_ext_irq_perf_reg(int irq) +{ + if (irq < 4) + return ext_irq_cfg_reg1; + return ext_irq_cfg_reg2; +} + +static inline void handle_internal(int intbit) +{ + if (is_ext_irq_cascaded && + intbit >= ext_irq_start && intbit <= ext_irq_end) + do_IRQ(intbit - ext_irq_start + IRQ_EXTERNAL_BASE); + else + do_IRQ(intbit + IRQ_INTERNAL_BASE); +} + +static inline int enable_irq_for_cpu(int cpu, struct irq_data *d, + const struct cpumask *m) +{ + bool enable = cpu_online(cpu); + +#ifdef CONFIG_SMP + if (m) + enable &= cpumask_test_cpu(cpu, m); + else if (irqd_affinity_was_set(d)) + enable &= cpumask_test_cpu(cpu, irq_data_get_affinity_mask(d)); +#endif + return enable; +} + +/* + * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not + * prioritize any interrupt relatively to another. the static counter + * will resume the loop where it ended the last time we left this + * function. + */ + +#define BUILD_IPIC_INTERNAL(width) \ +void __dispatch_internal_##width(int cpu) \ +{ \ + u32 pending[width / 32]; \ + unsigned int src, tgt; \ + bool irqs_pending = false; \ + static unsigned int i[2]; \ + unsigned int *next = &i[cpu]; \ + unsigned long flags; \ + \ + /* read registers in reverse order */ \ + spin_lock_irqsave(&ipic_lock, flags); \ + for (src = 0, tgt = (width / 32); src < (width / 32); src++) { \ + u32 val; \ + \ + val = bcm_readl(irq_stat_addr[cpu] + src * sizeof(u32)); \ + val &= bcm_readl(irq_mask_addr[cpu] + src * sizeof(u32)); \ + pending[--tgt] = val; \ + \ + if (val) \ + irqs_pending = true; \ + } \ + spin_unlock_irqrestore(&ipic_lock, flags); \ + \ + if (!irqs_pending) \ + return; \ + \ + while (1) { \ + unsigned int to_call = *next; \ + \ + *next = (*next + 1) & (width - 1); \ + if (pending[to_call / 32] & (1 << (to_call & 0x1f))) { \ + handle_internal(to_call); \ + break; \ + } \ + } \ +} \ + \ +static void __internal_irq_mask_##width(struct irq_data *d) \ +{ \ + u32 val; \ + unsigned irq = d->irq - IRQ_INTERNAL_BASE; \ + unsigned reg = (irq / 32) ^ (width/32 - 1); \ + unsigned bit = irq & 0x1f; \ + unsigned long flags; \ + int cpu; \ + \ + spin_lock_irqsave(&ipic_lock, flags); \ + for_each_present_cpu(cpu) { \ + if (!irq_mask_addr[cpu]) \ + break; \ + \ + val = bcm_readl(irq_mask_addr[cpu] + reg * sizeof(u32));\ + val &= ~(1 << bit); \ + bcm_writel(val, irq_mask_addr[cpu] + reg * sizeof(u32));\ + } \ + spin_unlock_irqrestore(&ipic_lock, flags); \ +} \ + \ +static void __internal_irq_unmask_##width(struct irq_data *d, \ + const struct cpumask *m) \ +{ \ + u32 val; \ + unsigned irq = d->irq - IRQ_INTERNAL_BASE; \ + unsigned reg = (irq / 32) ^ (width/32 - 1); \ + unsigned bit = irq & 0x1f; \ + unsigned long flags; \ + int cpu; \ + \ + spin_lock_irqsave(&ipic_lock, flags); \ + for_each_present_cpu(cpu) { \ + if (!irq_mask_addr[cpu]) \ + break; \ + \ + val = bcm_readl(irq_mask_addr[cpu] + reg * sizeof(u32));\ + if (enable_irq_for_cpu(cpu, d, m)) \ + val |= (1 << bit); \ + else \ + val &= ~(1 << bit); \ + bcm_writel(val, irq_mask_addr[cpu] + reg * sizeof(u32));\ + } \ + spin_unlock_irqrestore(&ipic_lock, flags); \ +} + +BUILD_IPIC_INTERNAL(32); +BUILD_IPIC_INTERNAL(64); + +asmlinkage void plat_irq_dispatch(void) +{ + u32 cause; + + do { + cause = read_c0_cause() & read_c0_status() & ST0_IM; + + if (!cause) + break; + + if (cause & CAUSEF_IP7) + do_IRQ(7); + if (cause & CAUSEF_IP0) + do_IRQ(0); + if (cause & CAUSEF_IP1) + do_IRQ(1); + if (cause & CAUSEF_IP2) + dispatch_internal(0); + if (is_ext_irq_cascaded) { + if (cause & CAUSEF_IP3) + dispatch_internal(1); + } else { + if (cause & CAUSEF_IP3) + do_IRQ(IRQ_EXT_0); + if (cause & CAUSEF_IP4) + do_IRQ(IRQ_EXT_1); + if (cause & CAUSEF_IP5) + do_IRQ(IRQ_EXT_2); + if (cause & CAUSEF_IP6) + do_IRQ(IRQ_EXT_3); + } + } while (1); +} + +/* + * internal IRQs operations: only mask/unmask on PERF irq mask + * register. + */ +static void bcm63xx_internal_irq_mask(struct irq_data *d) +{ + internal_irq_mask(d); +} + +static void bcm63xx_internal_irq_unmask(struct irq_data *d) +{ + internal_irq_unmask(d, NULL); +} + +/* + * external IRQs operations: mask/unmask and clear on PERF external + * irq control register. + */ +static void bcm63xx_external_irq_mask(struct irq_data *d) +{ + unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; + u32 reg, regaddr; + unsigned long flags; + + regaddr = get_ext_irq_perf_reg(irq); + spin_lock_irqsave(&epic_lock, flags); + reg = bcm_perf_readl(regaddr); + + if (BCMCPU_IS_6348()) + reg &= ~EXTIRQ_CFG_MASK_6348(irq % 4); + else + reg &= ~EXTIRQ_CFG_MASK(irq % 4); + + bcm_perf_writel(reg, regaddr); + spin_unlock_irqrestore(&epic_lock, flags); + + if (is_ext_irq_cascaded) + internal_irq_mask(irq_get_irq_data(irq + ext_irq_start)); +} + +static void bcm63xx_external_irq_unmask(struct irq_data *d) +{ + unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; + u32 reg, regaddr; + unsigned long flags; + + regaddr = get_ext_irq_perf_reg(irq); + spin_lock_irqsave(&epic_lock, flags); + reg = bcm_perf_readl(regaddr); + + if (BCMCPU_IS_6348()) + reg |= EXTIRQ_CFG_MASK_6348(irq % 4); + else + reg |= EXTIRQ_CFG_MASK(irq % 4); + + bcm_perf_writel(reg, regaddr); + spin_unlock_irqrestore(&epic_lock, flags); + + if (is_ext_irq_cascaded) + internal_irq_unmask(irq_get_irq_data(irq + ext_irq_start), + NULL); +} + +static void bcm63xx_external_irq_clear(struct irq_data *d) +{ + unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; + u32 reg, regaddr; + unsigned long flags; + + regaddr = get_ext_irq_perf_reg(irq); + spin_lock_irqsave(&epic_lock, flags); + reg = bcm_perf_readl(regaddr); + + if (BCMCPU_IS_6348()) + reg |= EXTIRQ_CFG_CLEAR_6348(irq % 4); + else + reg |= EXTIRQ_CFG_CLEAR(irq % 4); + + bcm_perf_writel(reg, regaddr); + spin_unlock_irqrestore(&epic_lock, flags); +} + +static int bcm63xx_external_irq_set_type(struct irq_data *d, + unsigned int flow_type) +{ + unsigned int irq = d->irq - IRQ_EXTERNAL_BASE; + u32 reg, regaddr; + int levelsense, sense, bothedge; + unsigned long flags; + + flow_type &= IRQ_TYPE_SENSE_MASK; + + if (flow_type == IRQ_TYPE_NONE) + flow_type = IRQ_TYPE_LEVEL_LOW; + + levelsense = sense = bothedge = 0; + switch (flow_type) { + case IRQ_TYPE_EDGE_BOTH: + bothedge = 1; + break; + + case IRQ_TYPE_EDGE_RISING: + sense = 1; + break; + + case IRQ_TYPE_EDGE_FALLING: + break; + + case IRQ_TYPE_LEVEL_HIGH: + levelsense = 1; + sense = 1; + break; + + case IRQ_TYPE_LEVEL_LOW: + levelsense = 1; + break; + + default: + pr_err("bogus flow type combination given !\n"); + return -EINVAL; + } + + regaddr = get_ext_irq_perf_reg(irq); + spin_lock_irqsave(&epic_lock, flags); + reg = bcm_perf_readl(regaddr); + irq %= 4; + + switch (bcm63xx_get_cpu_id()) { + case BCM6348_CPU_ID: + if (levelsense) + reg |= EXTIRQ_CFG_LEVELSENSE_6348(irq); + else + reg &= ~EXTIRQ_CFG_LEVELSENSE_6348(irq); + if (sense) + reg |= EXTIRQ_CFG_SENSE_6348(irq); + else + reg &= ~EXTIRQ_CFG_SENSE_6348(irq); + if (bothedge) + reg |= EXTIRQ_CFG_BOTHEDGE_6348(irq); + else + reg &= ~EXTIRQ_CFG_BOTHEDGE_6348(irq); + break; + + case BCM3368_CPU_ID: + case BCM6328_CPU_ID: + case BCM6338_CPU_ID: + case BCM6345_CPU_ID: + case BCM6358_CPU_ID: + case BCM6362_CPU_ID: + case BCM6368_CPU_ID: + if (levelsense) + reg |= EXTIRQ_CFG_LEVELSENSE(irq); + else + reg &= ~EXTIRQ_CFG_LEVELSENSE(irq); + if (sense) + reg |= EXTIRQ_CFG_SENSE(irq); + else + reg &= ~EXTIRQ_CFG_SENSE(irq); + if (bothedge) + reg |= EXTIRQ_CFG_BOTHEDGE(irq); + else + reg &= ~EXTIRQ_CFG_BOTHEDGE(irq); + break; + default: + BUG(); + } + + bcm_perf_writel(reg, regaddr); + spin_unlock_irqrestore(&epic_lock, flags); + + irqd_set_trigger_type(d, flow_type); + if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) + irq_set_handler_locked(d, handle_level_irq); + else + irq_set_handler_locked(d, handle_edge_irq); + + return IRQ_SET_MASK_OK_NOCOPY; +} + +#ifdef CONFIG_SMP +static int bcm63xx_internal_set_affinity(struct irq_data *data, + const struct cpumask *dest, + bool force) +{ + if (!irqd_irq_disabled(data)) + internal_irq_unmask(data, dest); + + return 0; +} +#endif + +static struct irq_chip bcm63xx_internal_irq_chip = { + .name = "bcm63xx_ipic", + .irq_mask = bcm63xx_internal_irq_mask, + .irq_unmask = bcm63xx_internal_irq_unmask, +}; + +static struct irq_chip bcm63xx_external_irq_chip = { + .name = "bcm63xx_epic", + .irq_ack = bcm63xx_external_irq_clear, + + .irq_mask = bcm63xx_external_irq_mask, + .irq_unmask = bcm63xx_external_irq_unmask, + + .irq_set_type = bcm63xx_external_irq_set_type, +}; + +static void bcm63xx_init_irq(void) +{ + int irq_bits; + + irq_stat_addr[0] = bcm63xx_regset_address(RSET_PERF); + irq_mask_addr[0] = bcm63xx_regset_address(RSET_PERF); + irq_stat_addr[1] = bcm63xx_regset_address(RSET_PERF); + irq_mask_addr[1] = bcm63xx_regset_address(RSET_PERF); + + switch (bcm63xx_get_cpu_id()) { + case BCM3368_CPU_ID: + irq_stat_addr[0] += PERF_IRQSTAT_3368_REG; + irq_mask_addr[0] += PERF_IRQMASK_3368_REG; + irq_stat_addr[1] = 0; + irq_mask_addr[1] = 0; + irq_bits = 32; + ext_irq_count = 4; + ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_3368; + break; + case BCM6328_CPU_ID: + irq_stat_addr[0] += PERF_IRQSTAT_6328_REG(0); + irq_mask_addr[0] += PERF_IRQMASK_6328_REG(0); + irq_stat_addr[1] += PERF_IRQSTAT_6328_REG(1); + irq_mask_addr[1] += PERF_IRQMASK_6328_REG(1); + irq_bits = 64; + ext_irq_count = 4; + is_ext_irq_cascaded = 1; + ext_irq_start = BCM_6328_EXT_IRQ0 - IRQ_INTERNAL_BASE; + ext_irq_end = BCM_6328_EXT_IRQ3 - IRQ_INTERNAL_BASE; + ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6328; + break; + case BCM6338_CPU_ID: + irq_stat_addr[0] += PERF_IRQSTAT_6338_REG; + irq_mask_addr[0] += PERF_IRQMASK_6338_REG; + irq_stat_addr[1] = 0; + irq_mask_addr[1] = 0; + irq_bits = 32; + ext_irq_count = 4; + ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6338; + break; + case BCM6345_CPU_ID: + irq_stat_addr[0] += PERF_IRQSTAT_6345_REG; + irq_mask_addr[0] += PERF_IRQMASK_6345_REG; + irq_stat_addr[1] = 0; + irq_mask_addr[1] = 0; + irq_bits = 32; + ext_irq_count = 4; + ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6345; + break; + case BCM6348_CPU_ID: + irq_stat_addr[0] += PERF_IRQSTAT_6348_REG; + irq_mask_addr[0] += PERF_IRQMASK_6348_REG; + irq_stat_addr[1] = 0; + irq_mask_addr[1] = 0; + irq_bits = 32; + ext_irq_count = 4; + ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6348; + break; + case BCM6358_CPU_ID: + irq_stat_addr[0] += PERF_IRQSTAT_6358_REG(0); + irq_mask_addr[0] += PERF_IRQMASK_6358_REG(0); + irq_stat_addr[1] += PERF_IRQSTAT_6358_REG(1); + irq_mask_addr[1] += PERF_IRQMASK_6358_REG(1); + irq_bits = 32; + ext_irq_count = 4; + is_ext_irq_cascaded = 1; + ext_irq_start = BCM_6358_EXT_IRQ0 - IRQ_INTERNAL_BASE; + ext_irq_end = BCM_6358_EXT_IRQ3 - IRQ_INTERNAL_BASE; + ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6358; + break; + case BCM6362_CPU_ID: + irq_stat_addr[0] += PERF_IRQSTAT_6362_REG(0); + irq_mask_addr[0] += PERF_IRQMASK_6362_REG(0); + irq_stat_addr[1] += PERF_IRQSTAT_6362_REG(1); + irq_mask_addr[1] += PERF_IRQMASK_6362_REG(1); + irq_bits = 64; + ext_irq_count = 4; + is_ext_irq_cascaded = 1; + ext_irq_start = BCM_6362_EXT_IRQ0 - IRQ_INTERNAL_BASE; + ext_irq_end = BCM_6362_EXT_IRQ3 - IRQ_INTERNAL_BASE; + ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6362; + break; + case BCM6368_CPU_ID: + irq_stat_addr[0] += PERF_IRQSTAT_6368_REG(0); + irq_mask_addr[0] += PERF_IRQMASK_6368_REG(0); + irq_stat_addr[1] += PERF_IRQSTAT_6368_REG(1); + irq_mask_addr[1] += PERF_IRQMASK_6368_REG(1); + irq_bits = 64; + ext_irq_count = 6; + is_ext_irq_cascaded = 1; + ext_irq_start = BCM_6368_EXT_IRQ0 - IRQ_INTERNAL_BASE; + ext_irq_end = BCM_6368_EXT_IRQ5 - IRQ_INTERNAL_BASE; + ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6368; + ext_irq_cfg_reg2 = PERF_EXTIRQ_CFG_REG2_6368; + break; + default: + BUG(); + } + + if (irq_bits == 32) { + dispatch_internal = __dispatch_internal_32; + internal_irq_mask = __internal_irq_mask_32; + internal_irq_unmask = __internal_irq_unmask_32; + } else { + dispatch_internal = __dispatch_internal_64; + internal_irq_mask = __internal_irq_mask_64; + internal_irq_unmask = __internal_irq_unmask_64; + } +} + +void __init arch_init_irq(void) +{ + int i, irq; + + bcm63xx_init_irq(); + mips_cpu_irq_init(); + for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i) + irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip, + handle_level_irq); + + for (i = IRQ_EXTERNAL_BASE; i < IRQ_EXTERNAL_BASE + ext_irq_count; ++i) + irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip, + handle_edge_irq); + + if (!is_ext_irq_cascaded) { + for (i = 3; i < 3 + ext_irq_count; ++i) { + irq = MIPS_CPU_IRQ_BASE + i; + if (request_irq(irq, no_action, IRQF_NO_THREAD, + "cascade_extirq", NULL)) { + pr_err("Failed to request irq %d (cascade_extirq)\n", + irq); + } + } + } + + irq = MIPS_CPU_IRQ_BASE + 2; + if (request_irq(irq, no_action, IRQF_NO_THREAD, "cascade_ip2", NULL)) + pr_err("Failed to request irq %d (cascade_ip2)\n", irq); +#ifdef CONFIG_SMP + if (is_ext_irq_cascaded) { + irq = MIPS_CPU_IRQ_BASE + 3; + if (request_irq(irq, no_action, IRQF_NO_THREAD, "cascade_ip3", + NULL)) + pr_err("Failed to request irq %d (cascade_ip3)\n", irq); + bcm63xx_internal_irq_chip.irq_set_affinity = + bcm63xx_internal_set_affinity; + + cpumask_clear(irq_default_affinity); + cpumask_set_cpu(smp_processor_id(), irq_default_affinity); + } +#endif +} diff --git a/arch/mips/bcm63xx/nvram.c b/arch/mips/bcm63xx/nvram.c new file mode 100644 index 000000000..05757aed0 --- /dev/null +++ b/arch/mips/bcm63xx/nvram.c @@ -0,0 +1,98 @@ +/* + * 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> + * Copyright (C) 2012 Jonas Gorski <jonas.gorski@gmail.com> + */ + +#define pr_fmt(fmt) "bcm63xx_nvram: " fmt + +#include <linux/bcm963xx_nvram.h> +#include <linux/init.h> +#include <linux/crc32.h> +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/if_ether.h> + +#include <bcm63xx_nvram.h> + +#define BCM63XX_DEFAULT_PSI_SIZE 64 + +static struct bcm963xx_nvram nvram; +static int mac_addr_used; + +void __init bcm63xx_nvram_init(void *addr) +{ + u32 crc, expected_crc; + u8 hcs_mac_addr[ETH_ALEN] = { 0x00, 0x10, 0x18, 0xff, 0xff, 0xff }; + + /* extract nvram data */ + memcpy(&nvram, addr, BCM963XX_NVRAM_V5_SIZE); + + /* check checksum before using data */ + if (bcm963xx_nvram_checksum(&nvram, &expected_crc, &crc)) + pr_warn("nvram checksum failed, contents may be invalid (expected %08x, got %08x)\n", + expected_crc, crc); + + /* Cable modems have a different NVRAM which is embedded in the eCos + * firmware and not easily extractible, give at least a MAC address + * pool. + */ + if (BCMCPU_IS_3368()) { + memcpy(nvram.mac_addr_base, hcs_mac_addr, ETH_ALEN); + nvram.mac_addr_count = 2; + } +} + +u8 *bcm63xx_nvram_get_name(void) +{ + return nvram.name; +} +EXPORT_SYMBOL(bcm63xx_nvram_get_name); + +int bcm63xx_nvram_get_mac_address(u8 *mac) +{ + u8 *oui; + int count; + + if (mac_addr_used >= nvram.mac_addr_count) { + pr_err("not enough mac addresses\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) { + pr_err("unable to fetch mac address\n"); + return -ENODEV; + } + } + + mac_addr_used++; + return 0; +} +EXPORT_SYMBOL(bcm63xx_nvram_get_mac_address); + +int bcm63xx_nvram_get_psi_size(void) +{ + if (nvram.psi_size > 0) + return nvram.psi_size; + + return BCM63XX_DEFAULT_PSI_SIZE; +} +EXPORT_SYMBOL(bcm63xx_nvram_get_psi_size); diff --git a/arch/mips/bcm63xx/prom.c b/arch/mips/bcm63xx/prom.c new file mode 100644 index 000000000..c3a2ea62c --- /dev/null +++ b/arch/mips/bcm63xx/prom.c @@ -0,0 +1,96 @@ +/* + * 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> + */ + +#include <linux/init.h> +#include <linux/memblock.h> +#include <linux/smp.h> +#include <asm/bootinfo.h> +#include <asm/bmips.h> +#include <asm/smp-ops.h> +#include <asm/mipsregs.h> +#include <bcm63xx_board.h> +#include <bcm63xx_cpu.h> +#include <bcm63xx_io.h> +#include <bcm63xx_regs.h> + +void __init prom_init(void) +{ + u32 reg, mask; + + bcm63xx_cpu_init(); + + /* stop any running watchdog */ + bcm_wdt_writel(WDT_STOP_1, WDT_CTL_REG); + bcm_wdt_writel(WDT_STOP_2, WDT_CTL_REG); + + /* disable all hardware blocks clock for now */ + if (BCMCPU_IS_3368()) + mask = CKCTL_3368_ALL_SAFE_EN; + else if (BCMCPU_IS_6328()) + mask = CKCTL_6328_ALL_SAFE_EN; + else if (BCMCPU_IS_6338()) + mask = CKCTL_6338_ALL_SAFE_EN; + else if (BCMCPU_IS_6345()) + mask = CKCTL_6345_ALL_SAFE_EN; + else if (BCMCPU_IS_6348()) + mask = CKCTL_6348_ALL_SAFE_EN; + else if (BCMCPU_IS_6358()) + mask = CKCTL_6358_ALL_SAFE_EN; + else if (BCMCPU_IS_6362()) + mask = CKCTL_6362_ALL_SAFE_EN; + else if (BCMCPU_IS_6368()) + mask = CKCTL_6368_ALL_SAFE_EN; + else + mask = 0; + + reg = bcm_perf_readl(PERF_CKCTL_REG); + reg &= ~mask; + bcm_perf_writel(reg, PERF_CKCTL_REG); + + /* do low level board init */ + board_prom_init(); + + /* set up SMP */ + if (!register_bmips_smp_ops()) { + /* + * BCM6328 might not have its second CPU enabled, while BCM3368 + * and BCM6358 need special handling for their shared TLB, so + * disable SMP for now. + */ + if (BCMCPU_IS_6328()) { + reg = bcm_readl(BCM_6328_OTP_BASE + + OTP_USER_BITS_6328_REG(3)); + + if (reg & OTP_6328_REG3_TP1_DISABLED) + bmips_smp_enabled = 0; + } else if (BCMCPU_IS_3368() || BCMCPU_IS_6358()) { + bmips_smp_enabled = 0; + } + + if (!bmips_smp_enabled) + return; + + /* + * The bootloader has set up the CPU1 reset vector at + * 0xa000_0200. + * This conflicts with the special interrupt vector (IV). + * The bootloader has also set up CPU1 to respond to the wrong + * IPI interrupt. + * Here we will start up CPU1 in the background and ask it to + * reconfigure itself then go back to sleep. + */ + memcpy((void *)0xa0000200, bmips_smp_movevec, 0x20); + __sync(); + set_c0_cause(C_SW0); + cpumask_set_cpu(1, &bmips_booted_mask); + + /* + * FIXME: we really should have some sort of hazard barrier here + */ + } +} diff --git a/arch/mips/bcm63xx/reset.c b/arch/mips/bcm63xx/reset.c new file mode 100644 index 000000000..64574e74c --- /dev/null +++ b/arch/mips/bcm63xx/reset.c @@ -0,0 +1,219 @@ +/* + * 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) 2012 Jonas Gorski <jonas.gorski@gmail.com> + */ + +#include <linux/init.h> +#include <linux/export.h> +#include <linux/mutex.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <bcm63xx_cpu.h> +#include <bcm63xx_io.h> +#include <bcm63xx_regs.h> +#include <bcm63xx_reset.h> + +#define __GEN_RESET_BITS_TABLE(__cpu) \ + [BCM63XX_RESET_SPI] = BCM## __cpu ##_RESET_SPI, \ + [BCM63XX_RESET_ENET] = BCM## __cpu ##_RESET_ENET, \ + [BCM63XX_RESET_USBH] = BCM## __cpu ##_RESET_USBH, \ + [BCM63XX_RESET_USBD] = BCM## __cpu ##_RESET_USBD, \ + [BCM63XX_RESET_DSL] = BCM## __cpu ##_RESET_DSL, \ + [BCM63XX_RESET_SAR] = BCM## __cpu ##_RESET_SAR, \ + [BCM63XX_RESET_EPHY] = BCM## __cpu ##_RESET_EPHY, \ + [BCM63XX_RESET_ENETSW] = BCM## __cpu ##_RESET_ENETSW, \ + [BCM63XX_RESET_PCM] = BCM## __cpu ##_RESET_PCM, \ + [BCM63XX_RESET_MPI] = BCM## __cpu ##_RESET_MPI, \ + [BCM63XX_RESET_PCIE] = BCM## __cpu ##_RESET_PCIE, \ + [BCM63XX_RESET_PCIE_EXT] = BCM## __cpu ##_RESET_PCIE_EXT, + +#define BCM3368_RESET_SPI SOFTRESET_3368_SPI_MASK +#define BCM3368_RESET_ENET SOFTRESET_3368_ENET_MASK +#define BCM3368_RESET_USBH 0 +#define BCM3368_RESET_USBD SOFTRESET_3368_USBS_MASK +#define BCM3368_RESET_DSL 0 +#define BCM3368_RESET_SAR 0 +#define BCM3368_RESET_EPHY SOFTRESET_3368_EPHY_MASK +#define BCM3368_RESET_ENETSW 0 +#define BCM3368_RESET_PCM SOFTRESET_3368_PCM_MASK +#define BCM3368_RESET_MPI SOFTRESET_3368_MPI_MASK +#define BCM3368_RESET_PCIE 0 +#define BCM3368_RESET_PCIE_EXT 0 + +#define BCM6328_RESET_SPI SOFTRESET_6328_SPI_MASK +#define BCM6328_RESET_ENET 0 +#define BCM6328_RESET_USBH SOFTRESET_6328_USBH_MASK +#define BCM6328_RESET_USBD SOFTRESET_6328_USBS_MASK +#define BCM6328_RESET_DSL 0 +#define BCM6328_RESET_SAR SOFTRESET_6328_SAR_MASK +#define BCM6328_RESET_EPHY SOFTRESET_6328_EPHY_MASK +#define BCM6328_RESET_ENETSW SOFTRESET_6328_ENETSW_MASK +#define BCM6328_RESET_PCM SOFTRESET_6328_PCM_MASK +#define BCM6328_RESET_MPI 0 +#define BCM6328_RESET_PCIE \ + (SOFTRESET_6328_PCIE_MASK | \ + SOFTRESET_6328_PCIE_CORE_MASK | \ + SOFTRESET_6328_PCIE_HARD_MASK) +#define BCM6328_RESET_PCIE_EXT SOFTRESET_6328_PCIE_EXT_MASK + +#define BCM6338_RESET_SPI SOFTRESET_6338_SPI_MASK +#define BCM6338_RESET_ENET SOFTRESET_6338_ENET_MASK +#define BCM6338_RESET_USBH SOFTRESET_6338_USBH_MASK +#define BCM6338_RESET_USBD SOFTRESET_6338_USBS_MASK +#define BCM6338_RESET_DSL SOFTRESET_6338_ADSL_MASK +#define BCM6338_RESET_SAR SOFTRESET_6338_SAR_MASK +#define BCM6338_RESET_EPHY 0 +#define BCM6338_RESET_ENETSW 0 +#define BCM6338_RESET_PCM 0 +#define BCM6338_RESET_MPI 0 +#define BCM6338_RESET_PCIE 0 +#define BCM6338_RESET_PCIE_EXT 0 + +#define BCM6348_RESET_SPI SOFTRESET_6348_SPI_MASK +#define BCM6348_RESET_ENET SOFTRESET_6348_ENET_MASK +#define BCM6348_RESET_USBH SOFTRESET_6348_USBH_MASK +#define BCM6348_RESET_USBD SOFTRESET_6348_USBS_MASK +#define BCM6348_RESET_DSL SOFTRESET_6348_ADSL_MASK +#define BCM6348_RESET_SAR SOFTRESET_6348_SAR_MASK +#define BCM6348_RESET_EPHY 0 +#define BCM6348_RESET_ENETSW 0 +#define BCM6348_RESET_PCM 0 +#define BCM6348_RESET_MPI 0 +#define BCM6348_RESET_PCIE 0 +#define BCM6348_RESET_PCIE_EXT 0 + +#define BCM6358_RESET_SPI SOFTRESET_6358_SPI_MASK +#define BCM6358_RESET_ENET SOFTRESET_6358_ENET_MASK +#define BCM6358_RESET_USBH SOFTRESET_6358_USBH_MASK +#define BCM6358_RESET_USBD 0 +#define BCM6358_RESET_DSL SOFTRESET_6358_ADSL_MASK +#define BCM6358_RESET_SAR SOFTRESET_6358_SAR_MASK +#define BCM6358_RESET_EPHY SOFTRESET_6358_EPHY_MASK +#define BCM6358_RESET_ENETSW 0 +#define BCM6358_RESET_PCM SOFTRESET_6358_PCM_MASK +#define BCM6358_RESET_MPI SOFTRESET_6358_MPI_MASK +#define BCM6358_RESET_PCIE 0 +#define BCM6358_RESET_PCIE_EXT 0 + +#define BCM6362_RESET_SPI SOFTRESET_6362_SPI_MASK +#define BCM6362_RESET_ENET 0 +#define BCM6362_RESET_USBH SOFTRESET_6362_USBH_MASK +#define BCM6362_RESET_USBD SOFTRESET_6362_USBS_MASK +#define BCM6362_RESET_DSL 0 +#define BCM6362_RESET_SAR SOFTRESET_6362_SAR_MASK +#define BCM6362_RESET_EPHY SOFTRESET_6362_EPHY_MASK +#define BCM6362_RESET_ENETSW SOFTRESET_6362_ENETSW_MASK +#define BCM6362_RESET_PCM SOFTRESET_6362_PCM_MASK +#define BCM6362_RESET_MPI 0 +#define BCM6362_RESET_PCIE (SOFTRESET_6362_PCIE_MASK | \ + SOFTRESET_6362_PCIE_CORE_MASK) +#define BCM6362_RESET_PCIE_EXT SOFTRESET_6362_PCIE_EXT_MASK + +#define BCM6368_RESET_SPI SOFTRESET_6368_SPI_MASK +#define BCM6368_RESET_ENET 0 +#define BCM6368_RESET_USBH SOFTRESET_6368_USBH_MASK +#define BCM6368_RESET_USBD SOFTRESET_6368_USBS_MASK +#define BCM6368_RESET_DSL 0 +#define BCM6368_RESET_SAR SOFTRESET_6368_SAR_MASK +#define BCM6368_RESET_EPHY SOFTRESET_6368_EPHY_MASK +#define BCM6368_RESET_ENETSW SOFTRESET_6368_ENETSW_MASK +#define BCM6368_RESET_PCM SOFTRESET_6368_PCM_MASK +#define BCM6368_RESET_MPI SOFTRESET_6368_MPI_MASK +#define BCM6368_RESET_PCIE 0 +#define BCM6368_RESET_PCIE_EXT 0 + +/* + * core reset bits + */ +static const u32 bcm3368_reset_bits[] = { + __GEN_RESET_BITS_TABLE(3368) +}; + +static const u32 bcm6328_reset_bits[] = { + __GEN_RESET_BITS_TABLE(6328) +}; + +static const u32 bcm6338_reset_bits[] = { + __GEN_RESET_BITS_TABLE(6338) +}; + +static const u32 bcm6348_reset_bits[] = { + __GEN_RESET_BITS_TABLE(6348) +}; + +static const u32 bcm6358_reset_bits[] = { + __GEN_RESET_BITS_TABLE(6358) +}; + +static const u32 bcm6362_reset_bits[] = { + __GEN_RESET_BITS_TABLE(6362) +}; + +static const u32 bcm6368_reset_bits[] = { + __GEN_RESET_BITS_TABLE(6368) +}; + +const u32 *bcm63xx_reset_bits; +static int reset_reg; + +static int __init bcm63xx_reset_bits_init(void) +{ + if (BCMCPU_IS_3368()) { + reset_reg = PERF_SOFTRESET_6358_REG; + bcm63xx_reset_bits = bcm3368_reset_bits; + } else if (BCMCPU_IS_6328()) { + reset_reg = PERF_SOFTRESET_6328_REG; + bcm63xx_reset_bits = bcm6328_reset_bits; + } else if (BCMCPU_IS_6338()) { + reset_reg = PERF_SOFTRESET_REG; + bcm63xx_reset_bits = bcm6338_reset_bits; + } else if (BCMCPU_IS_6348()) { + reset_reg = PERF_SOFTRESET_REG; + bcm63xx_reset_bits = bcm6348_reset_bits; + } else if (BCMCPU_IS_6358()) { + reset_reg = PERF_SOFTRESET_6358_REG; + bcm63xx_reset_bits = bcm6358_reset_bits; + } else if (BCMCPU_IS_6362()) { + reset_reg = PERF_SOFTRESET_6362_REG; + bcm63xx_reset_bits = bcm6362_reset_bits; + } else if (BCMCPU_IS_6368()) { + reset_reg = PERF_SOFTRESET_6368_REG; + bcm63xx_reset_bits = bcm6368_reset_bits; + } + + return 0; +} + +static DEFINE_SPINLOCK(reset_mutex); + +static void __bcm63xx_core_set_reset(u32 mask, int enable) +{ + unsigned long flags; + u32 val; + + if (!mask) + return; + + spin_lock_irqsave(&reset_mutex, flags); + val = bcm_perf_readl(reset_reg); + + if (enable) + val &= ~mask; + else + val |= mask; + + bcm_perf_writel(val, reset_reg); + spin_unlock_irqrestore(&reset_mutex, flags); +} + +void bcm63xx_core_set_reset(enum bcm63xx_core_reset core, int reset) +{ + __bcm63xx_core_set_reset(bcm63xx_reset_bits[core], reset); +} +EXPORT_SYMBOL(bcm63xx_core_set_reset); + +postcore_initcall(bcm63xx_reset_bits_init); diff --git a/arch/mips/bcm63xx/setup.c b/arch/mips/bcm63xx/setup.c new file mode 100644 index 000000000..d811e3e03 --- /dev/null +++ b/arch/mips/bcm63xx/setup.c @@ -0,0 +1,170 @@ +/* + * 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> + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/memblock.h> +#include <linux/ioport.h> +#include <linux/pm.h> +#include <asm/bootinfo.h> +#include <asm/time.h> +#include <asm/reboot.h> +#include <asm/cacheflush.h> +#include <bcm63xx_board.h> +#include <bcm63xx_cpu.h> +#include <bcm63xx_regs.h> +#include <bcm63xx_io.h> +#include <bcm63xx_gpio.h> + +void bcm63xx_machine_halt(void) +{ + pr_info("System halted\n"); + while (1) + ; +} + +static void bcm6348_a1_reboot(void) +{ + u32 reg; + + /* soft reset all blocks */ + pr_info("soft-resetting all blocks ...\n"); + reg = bcm_perf_readl(PERF_SOFTRESET_REG); + reg &= ~SOFTRESET_6348_ALL; + bcm_perf_writel(reg, PERF_SOFTRESET_REG); + mdelay(10); + + reg = bcm_perf_readl(PERF_SOFTRESET_REG); + reg |= SOFTRESET_6348_ALL; + bcm_perf_writel(reg, PERF_SOFTRESET_REG); + mdelay(10); + + /* Jump to the power on address. */ + pr_info("jumping to reset vector.\n"); + /* set high vectors (base at 0xbfc00000 */ + set_c0_status(ST0_BEV | ST0_ERL); + /* run uncached in kseg0 */ + change_c0_config(CONF_CM_CMASK, CONF_CM_UNCACHED); + __flush_cache_all(); + /* remove all wired TLB entries */ + write_c0_wired(0); + __asm__ __volatile__( + "jr\t%0" + : + : "r" (0xbfc00000)); + while (1) + ; +} + +void bcm63xx_machine_reboot(void) +{ + u32 reg, perf_regs[2] = { 0, 0 }; + unsigned int i; + + /* mask and clear all external irq */ + switch (bcm63xx_get_cpu_id()) { + case BCM3368_CPU_ID: + perf_regs[0] = PERF_EXTIRQ_CFG_REG_3368; + break; + case BCM6328_CPU_ID: + perf_regs[0] = PERF_EXTIRQ_CFG_REG_6328; + break; + case BCM6338_CPU_ID: + perf_regs[0] = PERF_EXTIRQ_CFG_REG_6338; + break; + case BCM6345_CPU_ID: + perf_regs[0] = PERF_EXTIRQ_CFG_REG_6345; + break; + case BCM6348_CPU_ID: + perf_regs[0] = PERF_EXTIRQ_CFG_REG_6348; + break; + case BCM6358_CPU_ID: + perf_regs[0] = PERF_EXTIRQ_CFG_REG_6358; + break; + case BCM6362_CPU_ID: + perf_regs[0] = PERF_EXTIRQ_CFG_REG_6362; + break; + } + + for (i = 0; i < 2; i++) { + if (!perf_regs[i]) + break; + + reg = bcm_perf_readl(perf_regs[i]); + if (BCMCPU_IS_6348()) { + reg &= ~EXTIRQ_CFG_MASK_ALL_6348; + reg |= EXTIRQ_CFG_CLEAR_ALL_6348; + } else { + reg &= ~EXTIRQ_CFG_MASK_ALL; + reg |= EXTIRQ_CFG_CLEAR_ALL; + } + bcm_perf_writel(reg, perf_regs[i]); + } + + if (BCMCPU_IS_6348() && (bcm63xx_get_cpu_rev() == 0xa1)) + bcm6348_a1_reboot(); + + pr_info("triggering watchdog soft-reset...\n"); + if (BCMCPU_IS_6328()) { + bcm_wdt_writel(1, WDT_SOFTRESET_REG); + } else { + reg = bcm_perf_readl(PERF_SYS_PLL_CTL_REG); + reg |= SYS_PLL_SOFT_RESET; + bcm_perf_writel(reg, PERF_SYS_PLL_CTL_REG); + } + while (1) + ; +} + +static void __bcm63xx_machine_reboot(char *p) +{ + bcm63xx_machine_reboot(); +} + +/* + * return system type in /proc/cpuinfo + */ +const char *get_system_type(void) +{ + static char buf[128]; + snprintf(buf, sizeof(buf), "bcm63xx/%s (0x%04x/0x%02X)", + board_get_name(), + bcm63xx_get_cpu_id(), bcm63xx_get_cpu_rev()); + return buf; +} + +void __init plat_time_init(void) +{ + mips_hpt_frequency = bcm63xx_get_cpu_freq() / 2; +} + +void __init plat_mem_setup(void) +{ + memblock_add(0, bcm63xx_get_memory_size()); + + _machine_halt = bcm63xx_machine_halt; + _machine_restart = __bcm63xx_machine_reboot; + pm_power_off = bcm63xx_machine_halt; + + set_io_port_base(0); + ioport_resource.start = 0; + ioport_resource.end = ~0; + + board_setup(); +} + +int __init bcm63xx_register_devices(void) +{ + /* register gpiochip */ + bcm63xx_gpio_init(); + + return board_register_devices(); +} + +arch_initcall(bcm63xx_register_devices); diff --git a/arch/mips/bcm63xx/timer.c b/arch/mips/bcm63xx/timer.c new file mode 100644 index 000000000..a86065854 --- /dev/null +++ b/arch/mips/bcm63xx/timer.c @@ -0,0 +1,206 @@ +/* + * 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> + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/export.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <bcm63xx_cpu.h> +#include <bcm63xx_io.h> +#include <bcm63xx_timer.h> +#include <bcm63xx_regs.h> + +static DEFINE_RAW_SPINLOCK(timer_reg_lock); +static DEFINE_RAW_SPINLOCK(timer_data_lock); +static struct clk *periph_clk; + +static struct timer_data { + void (*cb)(void *); + void *data; +} timer_data[BCM63XX_TIMER_COUNT]; + +static irqreturn_t timer_interrupt(int irq, void *dev_id) +{ + u32 stat; + int i; + + raw_spin_lock(&timer_reg_lock); + stat = bcm_timer_readl(TIMER_IRQSTAT_REG); + bcm_timer_writel(stat, TIMER_IRQSTAT_REG); + raw_spin_unlock(&timer_reg_lock); + + for (i = 0; i < BCM63XX_TIMER_COUNT; i++) { + if (!(stat & TIMER_IRQSTAT_TIMER_CAUSE(i))) + continue; + + raw_spin_lock(&timer_data_lock); + if (!timer_data[i].cb) { + raw_spin_unlock(&timer_data_lock); + continue; + } + + timer_data[i].cb(timer_data[i].data); + raw_spin_unlock(&timer_data_lock); + } + + return IRQ_HANDLED; +} + +int bcm63xx_timer_enable(int id) +{ + u32 reg; + unsigned long flags; + + if (id >= BCM63XX_TIMER_COUNT) + return -EINVAL; + + raw_spin_lock_irqsave(&timer_reg_lock, flags); + + reg = bcm_timer_readl(TIMER_CTLx_REG(id)); + reg |= TIMER_CTL_ENABLE_MASK; + bcm_timer_writel(reg, TIMER_CTLx_REG(id)); + + reg = bcm_timer_readl(TIMER_IRQSTAT_REG); + reg |= TIMER_IRQSTAT_TIMER_IR_EN(id); + bcm_timer_writel(reg, TIMER_IRQSTAT_REG); + + raw_spin_unlock_irqrestore(&timer_reg_lock, flags); + return 0; +} + +EXPORT_SYMBOL(bcm63xx_timer_enable); + +int bcm63xx_timer_disable(int id) +{ + u32 reg; + unsigned long flags; + + if (id >= BCM63XX_TIMER_COUNT) + return -EINVAL; + + raw_spin_lock_irqsave(&timer_reg_lock, flags); + + reg = bcm_timer_readl(TIMER_CTLx_REG(id)); + reg &= ~TIMER_CTL_ENABLE_MASK; + bcm_timer_writel(reg, TIMER_CTLx_REG(id)); + + reg = bcm_timer_readl(TIMER_IRQSTAT_REG); + reg &= ~TIMER_IRQSTAT_TIMER_IR_EN(id); + bcm_timer_writel(reg, TIMER_IRQSTAT_REG); + + raw_spin_unlock_irqrestore(&timer_reg_lock, flags); + return 0; +} + +EXPORT_SYMBOL(bcm63xx_timer_disable); + +int bcm63xx_timer_register(int id, void (*callback)(void *data), void *data) +{ + unsigned long flags; + int ret; + + if (id >= BCM63XX_TIMER_COUNT || !callback) + return -EINVAL; + + ret = 0; + raw_spin_lock_irqsave(&timer_data_lock, flags); + if (timer_data[id].cb) { + ret = -EBUSY; + goto out; + } + + timer_data[id].cb = callback; + timer_data[id].data = data; + +out: + raw_spin_unlock_irqrestore(&timer_data_lock, flags); + return ret; +} + +EXPORT_SYMBOL(bcm63xx_timer_register); + +void bcm63xx_timer_unregister(int id) +{ + unsigned long flags; + + if (id >= BCM63XX_TIMER_COUNT) + return; + + raw_spin_lock_irqsave(&timer_data_lock, flags); + timer_data[id].cb = NULL; + raw_spin_unlock_irqrestore(&timer_data_lock, flags); +} + +EXPORT_SYMBOL(bcm63xx_timer_unregister); + +unsigned int bcm63xx_timer_countdown(unsigned int countdown_us) +{ + return (clk_get_rate(periph_clk) / (1000 * 1000)) * countdown_us; +} + +EXPORT_SYMBOL(bcm63xx_timer_countdown); + +int bcm63xx_timer_set(int id, int monotonic, unsigned int countdown_us) +{ + u32 reg, countdown; + unsigned long flags; + + if (id >= BCM63XX_TIMER_COUNT) + return -EINVAL; + + countdown = bcm63xx_timer_countdown(countdown_us); + if (countdown & ~TIMER_CTL_COUNTDOWN_MASK) + return -EINVAL; + + raw_spin_lock_irqsave(&timer_reg_lock, flags); + reg = bcm_timer_readl(TIMER_CTLx_REG(id)); + + if (monotonic) + reg &= ~TIMER_CTL_MONOTONIC_MASK; + else + reg |= TIMER_CTL_MONOTONIC_MASK; + + reg &= ~TIMER_CTL_COUNTDOWN_MASK; + reg |= countdown; + bcm_timer_writel(reg, TIMER_CTLx_REG(id)); + + raw_spin_unlock_irqrestore(&timer_reg_lock, flags); + return 0; +} + +EXPORT_SYMBOL(bcm63xx_timer_set); + +int bcm63xx_timer_init(void) +{ + int ret, irq; + u32 reg; + + reg = bcm_timer_readl(TIMER_IRQSTAT_REG); + reg &= ~TIMER_IRQSTAT_TIMER0_IR_EN; + reg &= ~TIMER_IRQSTAT_TIMER1_IR_EN; + reg &= ~TIMER_IRQSTAT_TIMER2_IR_EN; + bcm_timer_writel(reg, TIMER_IRQSTAT_REG); + + periph_clk = clk_get(NULL, "periph"); + if (IS_ERR(periph_clk)) + return -ENODEV; + + irq = bcm63xx_get_irq_number(IRQ_TIMER); + ret = request_irq(irq, timer_interrupt, 0, "bcm63xx_timer", NULL); + if (ret) { + pr_err("%s: failed to register irq\n", __func__); + return ret; + } + + return 0; +} + +arch_initcall(bcm63xx_timer_init); |