| /* |
| * Copyright (C) 2009 Broadcom Corporation |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| */ |
| |
| #include <linux/init.h> |
| #include <linux/types.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/mm.h> |
| #include <linux/sched.h> |
| #include <linux/spinlock.h> |
| #include <linux/bitops.h> |
| #include <linux/module.h> |
| #include <linux/version.h> |
| #include <linux/compiler.h> |
| #include <linux/mmc/sdhci-pltfm.h> |
| |
| #include <asm/mipsregs.h> |
| #include <asm/barrier.h> |
| #include <asm/cacheflush.h> |
| #include <asm/r4kcache.h> |
| #include <asm/asm-offsets.h> |
| #include <asm/inst.h> |
| #include <asm/fpu.h> |
| #include <asm/hazards.h> |
| #include <asm/cpu-features.h> |
| #include <asm/brcmstb/brcmstb.h> |
| #include <dma-coherence.h> |
| |
| #include "../drivers/mmc/host/sdhci.h" |
| |
| /* chip features */ |
| int brcm_sata_enabled; |
| int brcm_enet_enabled; |
| int brcm_pci_enabled; |
| int brcm_pcie_enabled; |
| int brcm_smp_enabled; |
| int brcm_enet1_enabled; |
| int brcm_moca_enabled; |
| int brcm_usb_enabled; |
| int brcm_pm_enabled; |
| |
| /* synchronize writes to shared registers */ |
| DEFINE_SPINLOCK(brcm_magnum_spinlock); |
| EXPORT_SYMBOL(brcm_magnum_spinlock); |
| |
| /*********************************************************************** |
| * Per-chip operations |
| ***********************************************************************/ |
| |
| int __init bchip_strap_ram_size(void) |
| { |
| u32 reg; |
| #if defined(CONFIG_BCM7405) |
| const unsigned int mc[] = { 32, 64, 128, 256 }; |
| const unsigned int mode[] = { 4, 2, 1, 0, 2, 1, 0, 0 }; |
| unsigned int tmp; |
| reg = BDEV_RD_F(SUN_TOP_CTRL_STRAP_VALUE_0, strap_ddr0_device_config); |
| tmp = mc[reg & 3]; |
| reg = BDEV_RD_F(SUN_TOP_CTRL_STRAP_VALUE_0, strap_ddr_configuration); |
| return tmp * mode[reg]; |
| #else |
| reg = 0; |
| return reg; |
| #endif |
| } |
| |
| #define ALT_CHIP_ID(chip, rev) do { \ |
| u32 arg_id = 0x ## chip; \ |
| const u8 rev_name[] = #rev; \ |
| u32 arg_rev = ((rev_name[0] - 'a') << 4) | (rev_name[1] - '0'); \ |
| if (!kernel_chip_id && arg_id == chip_id) { \ |
| kernel_chip_id = arg_id; \ |
| kernel_chip_rev = arg_rev; \ |
| } \ |
| } while (0) |
| |
| #define MAIN_CHIP_ID(chip, rev) do { \ |
| u32 arg_id = 0x ## chip; \ |
| const u8 rev_name[] = #rev; \ |
| u32 arg_rev = ((rev_name[0] - 'a') << 4) | (rev_name[1] - '0'); \ |
| if (!kernel_chip_id) { \ |
| kernel_chip_id = arg_id; \ |
| kernel_chip_rev = arg_rev; \ |
| } \ |
| } while (0) |
| |
| /* |
| * NOTE: This is a quick sanity test to catch known incompatibilities and |
| * obvious chip ID mismatches. It is not comprehensive. Higher revs may |
| * or may not maintain software compatibility. |
| * |
| * MAIN_CHIP_ID() must always be the final entry. |
| */ |
| |
| void __init bchip_check_compat(void) |
| { |
| u32 chip_id = BRCM_CHIP_ID(), chip_rev = BRCM_CHIP_REV(); |
| u32 kernel_chip_id = 0, kernel_chip_rev = 0; |
| |
| #if defined(CONFIG_BCM7125) |
| ALT_CHIP_ID(7019, c0); |
| ALT_CHIP_ID(7025, c0); |
| ALT_CHIP_ID(7116, c0); |
| ALT_CHIP_ID(7117, c0); |
| ALT_CHIP_ID(7119, c0); |
| ALT_CHIP_ID(7120, c0); |
| MAIN_CHIP_ID(7125, c0); |
| #elif defined(CONFIG_BCM7231) |
| MAIN_CHIP_ID(7231, a0); |
| #elif defined(CONFIG_BCM7340) |
| ALT_CHIP_ID(7350, b0); |
| MAIN_CHIP_ID(7340, b0); |
| #elif defined(CONFIG_BCM7344) |
| MAIN_CHIP_ID(7344, a0); |
| #elif defined(CONFIG_BCM7346) |
| MAIN_CHIP_ID(7346, a0); |
| #elif defined(CONFIG_BCM7358) |
| /* 7358 kernel can boot on 7552, but not vice-versa */ |
| ALT_CHIP_ID(7552, a0); |
| MAIN_CHIP_ID(7358, a0); |
| #elif defined(CONFIG_BCM7552) |
| MAIN_CHIP_ID(7552, a0); |
| #elif defined(CONFIG_BCM7405) |
| ALT_CHIP_ID(7413, a0); |
| MAIN_CHIP_ID(7405, b0); |
| #elif defined(CONFIG_BCM7408) |
| MAIN_CHIP_ID(7408, b0); |
| #elif defined(CONFIG_BCM7420) |
| ALT_CHIP_ID(3320, c1); |
| ALT_CHIP_ID(7220, c1); |
| ALT_CHIP_ID(7409, c1); |
| ALT_CHIP_ID(7410, c1); |
| MAIN_CHIP_ID(7420, c1); |
| #elif defined(CONFIG_BCM7425) |
| MAIN_CHIP_ID(7425, a0); |
| #elif defined(CONFIG_BCM7468) |
| MAIN_CHIP_ID(7468, b0); |
| #elif defined(CONFIG_BCM7550) |
| MAIN_CHIP_ID(7550, a0); |
| #endif |
| if (!kernel_chip_id) |
| return; |
| |
| if (chip_id != kernel_chip_id) |
| cfe_die("PANIC: BCM%04x kernel cannot boot on " |
| "BCM%04x chip.\n", kernel_chip_id, chip_id); |
| |
| if (chip_rev < kernel_chip_rev) |
| cfe_die("PANIC: This kernel requires BCM%04x rev >= %02X " |
| "(P%02x)\n", kernel_chip_id, |
| kernel_chip_rev + 0xa0, kernel_chip_rev + 0x10); |
| } |
| |
| /*********************************************************************** |
| * MIPS features, caches, and bus interface |
| ***********************************************************************/ |
| |
| void bchip_mips_setup(void) |
| { |
| #if defined(CONFIG_BMIPS3300) |
| |
| unsigned long cbr = BMIPS_GET_CBR(); |
| |
| /* Set BIU to async mode */ |
| set_c0_brcm_bus_pll(BIT(22)); |
| __sync(); |
| |
| #ifdef BCHP_MISB_BRIDGE_WG_MODE_N_TIMEOUT |
| /* Enable write gathering */ |
| BDEV_WR_RB(BCHP_MISB_BRIDGE_WG_MODE_N_TIMEOUT, 0x264); |
| |
| /* Enable split mode */ |
| BDEV_WR_RB(BCHP_MISB_BRIDGE_MISB_SPLIT_MODE, 0x1); |
| __sync(); |
| #endif |
| |
| /* put the BIU back in sync mode */ |
| clear_c0_brcm_bus_pll(BIT(22)); |
| |
| /* clear BHTD to enable branch history table */ |
| clear_c0_brcm_reset(BIT(16)); |
| |
| /* Flush and enable RAC */ |
| DEV_WR_RB(cbr + BMIPS_RAC_CONFIG, 0x100); |
| DEV_WR_RB(cbr + BMIPS_RAC_CONFIG, 0xf); |
| DEV_WR_RB(cbr + BMIPS_RAC_ADDRESS_RANGE, 0x0fff0000); |
| |
| #elif defined(CONFIG_BMIPS4380) |
| |
| unsigned long cbr = BMIPS_GET_CBR(); |
| |
| /* CRBMIPS438X-164: CBG workaround */ |
| switch (read_c0_prid()) { |
| case 0x2a040: |
| case 0x2a042: |
| case 0x2a044: |
| case 0x2a060: |
| DEV_UNSET(cbr + BMIPS_L2_CONFIG, 0x07000000); |
| } |
| |
| /* clear BHTD to enable branch history table */ |
| clear_c0_brcm_config_0(BIT(21)); |
| |
| /* XI enable */ |
| if (kernel_uses_smartmips_rixi) |
| set_c0_brcm_config_0(BIT(23)); |
| |
| #elif defined(CONFIG_BMIPS5000) |
| |
| /* enable RDHWR, BRDHWR */ |
| set_c0_brcm_config(BIT(17) | BIT(21)); |
| |
| if (kernel_uses_smartmips_rixi) { |
| /* XI enable */ |
| set_c0_brcm_config(BIT(27)); |
| |
| /* enable MIPS32R2 ROR instruction for XI TLB handlers */ |
| __asm__ __volatile__( |
| " li $8, 0x5a455048\n" |
| " .word 0x4088b00f\n" /* mtc0 $8, $22, 15 */ |
| " nop; nop; nop\n" |
| " .word 0x4008b008\n" /* mfc0 $8, $22, 8 */ |
| " lui $9, 0x0100\n" |
| " or $8, $9\n" |
| " .word 0x4088b008\n" /* mtc0 $8, $22, 8 */ |
| : : : "$8", "$9"); |
| } |
| |
| #endif |
| } |
| |
| /*********************************************************************** |
| * Common operations for all chips |
| ***********************************************************************/ |
| |
| #ifdef CONFIG_BRCM_HAS_SATA3 |
| |
| #ifdef CONFIG_CPU_BIG_ENDIAN |
| #define DATA_ENDIAN 2 /* AHCI->DDR inbound accesses */ |
| #define MMIO_ENDIAN 2 /* MIPS->AHCI outbound accesses */ |
| #else |
| #define DATA_ENDIAN 0 |
| #define MMIO_ENDIAN 0 |
| #endif /* CONFIG_CPU_BIG_ENDIAN */ |
| |
| /* SATA3 SSC per-port bitfield */ |
| static u32 sata3_enable_ssc; |
| |
| #define SATA3_MDIO_TXPMD_0_REG_BANK 0x1A0 |
| #define SATA3_MDIO_BRIDGE_BASE (BCHP_SATA_GRB_REG_START + 0x100) |
| #define SATA3_MDIO_BASE_REG_ADDR (SATA3_MDIO_BRIDGE_BASE + 0x8F * 4) |
| |
| #define SATA_AHCI_GHC_PORTS_IMPLEMENTED (BCHP_SATA_AHCI_GHC_REG_START + 0xC) |
| |
| #define SATA3_TXPMD_CONTROL1 0x81 |
| #define SATA3_TXPMD_TX_FREQ_CTRL_CONTROL1 0x82 |
| #define SATA3_TXPMD_TX_FREQ_CTRL_CONTROL2 0x83 |
| #define SATA3_TXPMD_TX_FREQ_CTRL_CONTROL3 0x84 |
| |
| static inline void brcm_sata3_mdio_wr_reg(u32 bank, unsigned int ofs, u32 msk, |
| u32 enable) |
| { |
| u32 tmp; |
| BDEV_WR(SATA3_MDIO_BASE_REG_ADDR, bank); |
| /* Read, mask, enable */ |
| tmp = BDEV_RD(ofs * 4 + SATA3_MDIO_BRIDGE_BASE); |
| tmp = (tmp & msk) | enable; |
| /* Write */ |
| BDEV_WR(ofs * 4 + SATA3_MDIO_BRIDGE_BASE, tmp); |
| } |
| |
| static void brcm_sata3_init_freq(int port, int ssc_enable) |
| { |
| u32 bank = SATA3_MDIO_TXPMD_0_REG_BANK + port * 0x10; |
| |
| if (ssc_enable) |
| pr_info("SATA3: enabling SSC on port %d\n", port); |
| |
| /* TXPMD_control1 - enable SSC force */ |
| brcm_sata3_mdio_wr_reg(bank, SATA3_TXPMD_CONTROL1, 0xFFFFFFFC, |
| 0x00000003); |
| |
| /* TXPMD_tx_freq_ctrl_control2 - set fixed min freq */ |
| brcm_sata3_mdio_wr_reg(bank, SATA3_TXPMD_TX_FREQ_CTRL_CONTROL2, |
| 0xFFFFFC00, 0x000003DF); |
| |
| /* |
| * TXPMD_tx_freq_ctrl_control3 - set fixed max freq |
| * If ssc_enable == 0, center frequencies |
| * Otherwise, spread spectrum frequencies |
| */ |
| if (ssc_enable) |
| brcm_sata3_mdio_wr_reg(bank, SATA3_TXPMD_TX_FREQ_CTRL_CONTROL3, |
| 0xFFFFFC00, 0x00000083); |
| else |
| brcm_sata3_mdio_wr_reg(bank, SATA3_TXPMD_TX_FREQ_CTRL_CONTROL3, |
| 0xFFFFFC00, 0x000003DF); |
| } |
| |
| /* Check up to 32 ports, although we typically only have 2 */ |
| #define SATA_MAX_CHECK_PORTS 32 |
| |
| /* |
| * Check commandline for 'sata3_ssc' options. They can be specified in 2 ways: |
| * (1) 'sata3_ssc' -> enable SSC on all ports |
| * (2) 'sata3_ssc=x,y' -> enable SSC on specific port(s), given a comma- |
| * separated list of port numbers |
| */ |
| static int __init sata3_ssc_setup(char *str) |
| { |
| int opts[SATA_MAX_CHECK_PORTS + 1], i; |
| |
| if (*str == '\0') { |
| /* enable SSC on all ports */ |
| sata3_enable_ssc = ~0; |
| return 0; |
| } |
| get_options(str + 1, SATA_MAX_CHECK_PORTS, opts); |
| |
| for (i = 0; i < opts[0]; i++) { |
| int port = opts[i + 1]; |
| if ((port >= 0) && (port < SATA_MAX_CHECK_PORTS)) |
| sata3_enable_ssc |= 1 << port; |
| } |
| |
| return 0; |
| } |
| |
| __setup("sata3_ssc", sata3_ssc_setup); |
| |
| #endif /* CONFIG_BRCM_HAS_SATA3 */ |
| |
| void bchip_sata3_init(void) |
| { |
| #ifdef CONFIG_BRCM_HAS_SATA3 |
| int i, ports = fls(BDEV_RD(SATA_AHCI_GHC_PORTS_IMPLEMENTED)); |
| |
| BDEV_WR(BCHP_SATA_TOP_CTRL_BUS_CTRL, (DATA_ENDIAN << 4) | |
| (DATA_ENDIAN << 2) | (MMIO_ENDIAN << 0)); |
| |
| for (i = 0; i < ports; i++) |
| brcm_sata3_init_freq(i, sata3_enable_ssc & (1 << i)); |
| #endif |
| } |
| |
| #ifdef CONFIG_CPU_LITTLE_ENDIAN |
| #define USB_ENDIAN 0x03 /* !WABO !FNBO FNHW BABO */ |
| #else |
| #define USB_ENDIAN 0x0e /* WABO FNBO FNHW !BABO */ |
| #endif |
| |
| #define USB_ENDIAN_MASK 0x0f |
| #define USB_IOC BCHP_USB_CTRL_SETUP_IOC_MASK |
| #define USB_IPP BCHP_USB_CTRL_SETUP_IPP_MASK |
| |
| #define USB_REG(x, y) (x + BCHP_USB_CTRL_##y - \ |
| BCHP_USB_CTRL_REG_START) |
| |
| static void bchip_usb_init_one(int id, uintptr_t base) |
| { |
| /* endianness setup */ |
| BDEV_UNSET_RB(USB_REG(base, SETUP), USB_ENDIAN_MASK); |
| BDEV_SET_RB(USB_REG(base, SETUP), USB_ENDIAN); |
| |
| /* power control setup */ |
| #ifdef CONFIG_BRCM_OVERRIDE_USB |
| |
| #ifdef CONFIG_BRCM_FORCE_USB_OC_LO |
| BDEV_SET(USB_REG(base, SETUP), USB_IOC); |
| #else |
| BDEV_UNSET(USB_REG(base, SETUP), USB_IOC); |
| #endif |
| |
| #ifdef CONFIG_BRCM_FORCE_USB_PWR_LO |
| BDEV_SET(USB_REG(base, SETUP), USB_IPP); |
| #else |
| BDEV_UNSET(USB_REG(base, SETUP), USB_IPP); |
| #endif |
| |
| #else /* CONFIG_BRCM_OVERRIDE_USB */ |
| if ((BDEV_RD(USB_REG(base, SETUP)) & USB_IOC) == 0) { |
| printk(KERN_WARNING "USB%d: IOC was not set by the bootloader;" |
| " forcing default settings\n", id); |
| BDEV_SET(USB_REG(base, SETUP), USB_IOC); |
| BDEV_SET(USB_REG(base, SETUP), USB_IPP); |
| } |
| #endif /* CONFIG_BRCM_OVERRIDE_USB */ |
| |
| printk(KERN_INFO "USB%d: power enable is active %s; overcurrent is " |
| "active %s\n", id, |
| BDEV_RD(USB_REG(base, SETUP)) & USB_IPP ? "low" : "high", |
| BDEV_RD(USB_REG(base, SETUP)) & USB_IOC ? "low" : "high"); |
| |
| /* PR45703 - for OHCI->SCB bridge lockup */ |
| BDEV_UNSET(USB_REG(base, OBRIDGE), |
| BCHP_USB_CTRL_OBRIDGE_OBR_SEQ_EN_MASK); |
| |
| /* Disable EHCI transaction combining */ |
| BDEV_UNSET(USB_REG(base, EBRIDGE), |
| BCHP_USB_CTRL_EBRIDGE_EBR_SEQ_EN_MASK); |
| |
| /* SWLINUX-1705: Avoid OUT packet underflows */ |
| BDEV_UNSET(USB_REG(base, EBRIDGE), |
| BCHP_USB_CTRL_EBRIDGE_EBR_SCB_SIZE_MASK); |
| BDEV_SET(USB_REG(base, EBRIDGE), |
| 0x08 << BCHP_USB_CTRL_EBRIDGE_EBR_SCB_SIZE_SHIFT); |
| |
| #if defined(CONFIG_BRCM_HAS_1GB_MEMC1) |
| /* enable access to SCB1 */ |
| BDEV_SET(USB_REG(base, SETUP), BIT(14)); |
| |
| #if defined(CONFIG_BCM7425B0) || defined(CONFIG_BCM7435A0) || \ |
| defined(CONFIG_BCM7435B0) |
| /* SWLINUX-2259 - Work around a USB DMA to memc1 arbitration bug */ |
| BDEV_SET(USB_REG(base, SETUP), BIT(13)); |
| #endif |
| |
| #endif |
| |
| #if defined(BCHP_USB_CTRL_GENERIC_CTL_1_PLL_SUSPEND_EN_MASK) |
| BDEV_SET(USB_REG(base, GENERIC_CTL_1), |
| BCHP_USB_CTRL_GENERIC_CTL_1_PLL_SUSPEND_EN_MASK); |
| #elif defined(BCHP_USB_CTRL_GENERIC_CTL_PLL_SUSPEND_EN_MASK) |
| BDEV_SET(USB_REG(base, GENERIC_CTL), |
| BCHP_USB_CTRL_GENERIC_CTL_PLL_SUSPEND_EN_MASK); |
| #elif defined(BCHP_USB_CTRL_PLL_CTL_1_PLL_SUSPEND_EN_MASK) |
| BDEV_SET(USB_REG(base, PLL_CTL_1), |
| BCHP_USB_CTRL_PLL_CTL_1_PLL_SUSPEND_EN_MASK); |
| #elif defined(BCHP_USB_CTRL_PLL_CTL_PLL_SUSPEND_EN_MASK) |
| BDEV_SET(USB_REG(base, PLL_CTL), |
| BCHP_USB_CTRL_PLL_CTL_PLL_SUSPEND_EN_MASK); |
| #endif |
| |
| } |
| |
| void bchip_usb_init(void) |
| { |
| bchip_usb_init_one(0, BCHP_USB_CTRL_REG_START); |
| #ifdef BCHP_USB1_CTRL_REG_START |
| bchip_usb_init_one(1, BCHP_USB1_CTRL_REG_START); |
| #endif |
| } |
| |
| #if defined(CONFIG_BRCM_HAS_MOCA) |
| void bchip_moca_init(void) |
| { |
| #ifdef BCHP_SUN_TOP_CTRL_SW_RESET |
| BDEV_WR_F_RB(SUN_TOP_CTRL_SW_RESET, moca_sw_reset, 0); |
| #else |
| BDEV_WR_F_RB(SUN_TOP_CTRL_SW_INIT_0_CLEAR, moca_sw_init, 1); |
| #endif |
| |
| #ifdef BCHP_MOCA_HOSTMISC_SW_RESET_moca_enet_reset_MASK |
| BDEV_WR_F_RB(MOCA_HOSTMISC_SW_RESET, moca_enet_reset, 0); |
| #endif |
| |
| #if defined(CONFIG_BCM7125) |
| BDEV_WR_F_RB(CLKGEN_MISC_CLOCK_SELECTS, CLOCK_SEL_ENET_CG_MOCA, 0); |
| BDEV_WR_F_RB(CLKGEN_MISC_CLOCK_SELECTS, CLOCK_SEL_GMII_CG_MOCA, 0); |
| #elif defined(CONFIG_BCM7340) |
| BDEV_WR_F_RB(CLKGEN_MISC_CLOCK_SELECTS, CLOCK_SEL_ENET_CG_MOCA, 1); |
| BDEV_WR_F_RB(CLKGEN_MISC_CLOCK_SELECTS, CLOCK_SEL_GMII_CG_MOCA, 0); |
| #elif defined(CONFIG_BCM7408) || defined(CONFIG_BCM7420) |
| BDEV_WR_F_RB(CLK_MISC, MOCA_ENET_GMII_TX_CLK_SEL, 0); |
| #endif |
| } |
| #endif |
| |
| #if defined(CONFIG_BRCM_SDIO) |
| |
| /* Use custom I/O accessors to avoid readl/readw byte swapping in BE mode */ |
| |
| static u32 sdhci_brcm_readl(struct sdhci_host *host, int reg) |
| { |
| return __raw_readl(host->ioaddr + reg); |
| } |
| |
| static u16 sdhci_brcm_readw(struct sdhci_host *host, int reg) |
| { |
| return __raw_readw(host->ioaddr + reg); |
| } |
| |
| static void sdhci_brcm_writel(struct sdhci_host *host, u32 val, int reg) |
| { |
| __raw_writel(val, host->ioaddr + reg); |
| } |
| |
| static void sdhci_brcm_writew(struct sdhci_host *host, u16 val, int reg) |
| { |
| __raw_writew(val, host->ioaddr + reg); |
| } |
| |
| static struct sdhci_ops __maybe_unused sdhci_be_ops = { |
| .read_l = sdhci_brcm_readl, |
| .read_w = sdhci_brcm_readw, |
| .write_l = sdhci_brcm_writel, |
| .write_w = sdhci_brcm_writew, |
| }; |
| |
| struct sdhci_pltfm_data sdhci_brcm_pdata = { }; |
| |
| static int nommc; |
| |
| static int __init nommc_setup(char *str) |
| { |
| nommc = 1; |
| return 0; |
| } |
| |
| __setup("nommc", nommc_setup); |
| |
| int bchip_sdio_init(int id, uintptr_t cfg_base) |
| { |
| #define SDIO_CFG_REG(x, y) (x + BCHP_SDIO_0_CFG_##y - \ |
| BCHP_SDIO_0_CFG_REG_START) |
| #define SDIO_CFG_SET(base, reg, mask) do { \ |
| BDEV_SET(SDIO_CFG_REG(base, reg), \ |
| BCHP_SDIO_0_CFG_##reg##_##mask##_MASK); \ |
| } while (0) |
| #define SDIO_CFG_UNSET(base, reg, mask) do { \ |
| BDEV_UNSET(SDIO_CFG_REG(base, reg), \ |
| BCHP_SDIO_0_CFG_##reg##_##mask##_MASK); \ |
| } while (0) |
| #define SDIO_CFG_FIELD(base, reg, field, val) do { \ |
| BDEV_UNSET(SDIO_CFG_REG(base, reg), \ |
| BCHP_SDIO_0_CFG_##reg##_##field##_MASK); \ |
| BDEV_SET(SDIO_CFG_REG(base, reg), \ |
| (val) << BCHP_SDIO_0_CFG_##reg##_##field##_SHIFT); \ |
| } while (0) |
| |
| if (nommc) { |
| printk(KERN_INFO "SDIO_%d: disabled via command line\n", id); |
| return -ENODEV; |
| } |
| if (BDEV_RD(SDIO_CFG_REG(cfg_base, SCRATCH)) & 0x01) { |
| printk(KERN_INFO "SDIO_%d: disabled by bootloader\n", id); |
| return -ENODEV; |
| } |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) |
| printk(KERN_INFO "SDIO_%d: this core requires Linux 2.6.37 or higher; " |
| "disabling\n", id); |
| return -ENODEV; |
| #endif |
| /* |
| * The following chips have SDIO issues and will not run correctly |
| * at 50MHz so disable them. |
| */ |
| #if defined(CONFIG_BCM7425B0) || defined(CONFIG_BCM7429A0) || \ |
| defined(CONFIG_BCM7435A0) |
| /* Enable just the 7425B2 */ |
| if ((BRCM_CHIP_ID() != 0x7425) || (BRCM_CHIP_REV() < 0x12)) { |
| printk(KERN_INFO "SDIO_%d: disabled due to chip issues\n", id); |
| return -ENODEV; |
| } else { |
| /* For 7425B2, use manual input clock tuning to work */ |
| /* around a chip problem, and disable UHS and TUNING. */ |
| SDIO_CFG_FIELD(cfg_base, CAP_REG0, SLOT_TYPE, 1); |
| SDIO_CFG_UNSET(cfg_base, CAP_REG0, DDR50_SUPPORT); |
| SDIO_CFG_UNSET(cfg_base, CAP_REG0, SDR50); |
| SDIO_CFG_UNSET(cfg_base, CAP_REG1, USE_TUNING); |
| SDIO_CFG_SET(cfg_base, CAP_REG1, CAP_REG_OVERRIDE); |
| |
| /* enable input delay, resolution = 1, value = 8 */ |
| SDIO_CFG_FIELD(cfg_base, IP_DLY, IP_TAP_DELAY, 8); |
| SDIO_CFG_FIELD(cfg_base, IP_DLY, IP_DELAY_CTRL, 1); |
| SDIO_CFG_SET(cfg_base, IP_DLY, IP_TAP_EN); |
| |
| /* Use the manual clock delay */ |
| SDIO_CFG_FIELD(cfg_base, SD_CLOCK_DELAY, INPUT_CLOCK_DELAY, 8); |
| } |
| |
| #endif |
| |
| printk(KERN_INFO "SDIO_%d: enabling controller\n", id); |
| BDEV_UNSET(SDIO_CFG_REG(cfg_base, SDIO_EMMC_CTRL1), 0xf000); |
| BDEV_UNSET(SDIO_CFG_REG(cfg_base, SDIO_EMMC_CTRL2), 0x00ff); |
| #ifdef CONFIG_CPU_LITTLE_ENDIAN |
| /* FRAME_NHW | BUFFER_ABO */ |
| BDEV_SET(SDIO_CFG_REG(cfg_base, SDIO_EMMC_CTRL1), 0x3000); |
| #else |
| /* WORD_ABO | FRAME_NBO | FRAME_NHW */ |
| BDEV_SET(SDIO_CFG_REG(cfg_base, SDIO_EMMC_CTRL1), 0xe000); |
| /* address swap only */ |
| BDEV_SET(SDIO_CFG_REG(cfg_base, SDIO_EMMC_CTRL2), 0x0050); |
| sdhci_brcm_pdata.ops = &sdhci_be_ops; |
| #endif |
| |
| #if defined(CONFIG_BCM7231B0) || defined(CONFIG_BCM7346B0) |
| BDEV_SET(SDIO_CFG_REG(cfg_base, CAP_REG1), BIT(31)); /* Override=1 */ |
| #endif |
| |
| #if defined(CONFIG_BCM7344B0) |
| BDEV_UNSET(SDIO_CFG_REG(cfg_base, CAP_REG0), BIT(19)); /* Highspd=0 */ |
| BDEV_SET(SDIO_CFG_REG(cfg_base, CAP_REG1), BIT(31)); /* Override=1 */ |
| #endif |
| |
| return 0; |
| } |
| #endif /* defined(CONFIG_BRCM_SDIO) */ |
| |
| void __init bchip_set_features(void) |
| { |
| #if defined(CONFIG_BRCM_HAS_SATA) |
| brcm_sata_enabled = 1; |
| #endif |
| #if defined(CONFIG_BRCM_HAS_EMAC_0) || defined(CONFIG_BRCM_HAS_GENET) |
| brcm_enet_enabled = 1; |
| #endif |
| #if defined(CONFIG_BRCM_HAS_PCI23) |
| brcm_pci_enabled = 1; |
| #endif |
| #if defined(CONFIG_BRCM_HAS_PCIE) |
| brcm_pcie_enabled = 1; |
| #endif |
| #if defined(CONFIG_SMP) |
| brcm_smp_enabled = 1; |
| #endif |
| #if defined(CONFIG_BRCM_HAS_EMAC_1) || defined(CONFIG_BRCM_HAS_GENET_1) |
| brcm_enet1_enabled = 1; |
| #endif |
| #if defined(CONFIG_BRCM_HAS_MOCA) |
| brcm_moca_enabled = 1; |
| #endif |
| #if defined(CONFIG_BRCM_PM) |
| brcm_pm_enabled = 1; |
| #endif |
| brcm_usb_enabled = 1; |
| |
| /* now remove any features disabled in hardware */ |
| |
| #ifdef CONFIG_BCM7405 |
| if (BDEV_RD_F(SUN_TOP_CTRL_OTP_OPTION_STATUS, otp_option_sata_disable)) |
| brcm_sata_enabled = 0; |
| |
| switch (BDEV_RD_F(SUN_TOP_CTRL_OTP_OPTION_STATUS, |
| otp_option_product_id)) { |
| case 0x0: |
| /* 7405/7406 */ |
| break; |
| case 0x1: |
| /* 7466 */ |
| brcm_pci_enabled = 0; |
| brcm_enet1_enabled = 0; |
| break; |
| case 0x3: |
| /* 7106 */ |
| brcm_enet1_enabled = 0; |
| brcm_smp_enabled = 0; |
| break; |
| case 0x4: |
| case 0x6: |
| /* 7205/7213 */ |
| brcm_enet1_enabled = 0; |
| break; |
| } |
| #endif |
| |
| #ifdef BCHP_SUN_TOP_CTRL_OTP_OPTION_STATUS_0_otp_option_sata_disable_MASK |
| if (BDEV_RD_F(SUN_TOP_CTRL_OTP_OPTION_STATUS_0, |
| otp_option_sata_disable) == 1) |
| brcm_sata_enabled = 0; |
| #endif |
| |
| #ifdef BCHP_SUN_TOP_CTRL_OTP_OPTION_STATUS_0_otp_option_enet_disable_MASK |
| if (BDEV_RD_F(SUN_TOP_CTRL_OTP_OPTION_STATUS_0, |
| otp_option_enet_disable) == 1) |
| brcm_enet_enabled = 0; |
| #endif |
| |
| #ifdef BCHP_SUN_TOP_CTRL_OTP_OPTION_STATUS_0_otp_option_usb_disable_MASK |
| if (BDEV_RD_F(SUN_TOP_CTRL_OTP_OPTION_STATUS_0, |
| otp_option_usb_disable) == 1) |
| brcm_usb_enabled = 0; |
| #endif |
| |
| #ifdef BCHP_SUN_TOP_CTRL_OTP_OPTION_STATUS_0_otp_option_moca_disable_MASK |
| if (BDEV_RD_F(SUN_TOP_CTRL_OTP_OPTION_STATUS_0, |
| otp_option_moca_disable) == 1) |
| brcm_moca_enabled = 0; |
| #endif |
| |
| #if defined(CONFIG_BCM7125) || defined(CONFIG_BCM7340) || \ |
| defined(CONFIG_BCM7420) |
| |
| switch (BRCM_CHIP_ID()) { |
| case 0x7340: |
| if (BDEV_RD_F(SUN_TOP_CTRL_OTP_OPTION_STATUS_0, |
| otp_option_product_id) != 1) |
| break; |
| /* 7350, 7352 (alt): fall through */ |
| case 0x7019: |
| case 0x7117: |
| case 0x7119: |
| case 0x7350: |
| case 0x7352: |
| case 0x7409: |
| if (brcm_moca_enabled) { |
| /* MOCA_GENET is usable - enable and scan it */ |
| BDEV_WR_F_RB(SUN_TOP_CTRL_SW_RESET, moca_sw_reset, 0); |
| BDEV_WR_F_RB(MOCA_HOSTMISC_SW_RESET, moca_enet_reset, |
| 0); |
| } else { |
| /* MOCA_GENET regs are inaccessible - don't touch it */ |
| brcm_enet1_enabled = 0; |
| } |
| |
| /* MoCA PHY is always disabled on these bondouts */ |
| brcm_moca_enabled = 0; |
| break; |
| } |
| #endif |
| |
| #ifdef BCHP_SUN_TOP_CTRL_OTP_OPTION_STATUS_0_otp_option_pcie_disable_MASK |
| if (BDEV_RD_F(SUN_TOP_CTRL_OTP_OPTION_STATUS_0, |
| otp_option_pcie_disable) == 1) |
| brcm_pcie_enabled = 0; |
| #endif |
| |
| #ifdef CONFIG_BCM7425 |
| /* disable PCIe initialization in EP mode */ |
| if (BDEV_RD_F(SUN_TOP_CTRL_STRAP_VALUE_0, strap_rc_ep) == 0) |
| brcm_pcie_enabled = 0; |
| #endif |
| } |
| |
| void __init bchip_early_setup(void) |
| { |
| #if defined(CONFIG_BRCM_HAS_WKTMR) |
| struct wktmr_time t; |
| |
| BDEV_WR_F_RB(WKTMR_EVENT, wktmr_alarm_event, 1); |
| BDEV_WR_F_RB(WKTMR_PRESCALER, wktmr_prescaler, WKTMR_FREQ); |
| BDEV_WR_F_RB(WKTMR_COUNTER, wktmr_counter, 0); |
| |
| /* wait for first tick so we know the counter is ready to use */ |
| wktmr_read(&t); |
| while (wktmr_elapsed(&t) == 0) |
| ; |
| #endif |
| |
| #ifdef CONFIG_PCI |
| if (brcm_pcie_enabled) |
| brcm_early_pcie_setup(); |
| #endif |
| } |
| |
| /*********************************************************************** |
| * Simulate privileged instructions (RDHWR, MFC0) and unaligned accesses |
| ***********************************************************************/ |
| |
| #define OPCODE 0xfc000000 |
| #define BASE 0x03e00000 |
| #define RT 0x001f0000 |
| #define OFFSET 0x0000ffff |
| #define LL 0xc0000000 |
| #define SC 0xe0000000 |
| #define SPEC0 0x00000000 |
| #define SPEC3 0x7c000000 |
| #define RD 0x0000f800 |
| #define FUNC 0x0000003f |
| #define SYNC 0x0000000f |
| #define RDHWR 0x0000003b |
| |
| #define BRDHWR 0xec000000 |
| #define OP_MFC0 0x40000000 |
| |
| int brcm_simulate_opcode(struct pt_regs *regs, unsigned int opcode) |
| { |
| struct thread_info *ti = task_thread_info(current); |
| int rd = (opcode & RD) >> 11; |
| int rt = (opcode & RT) >> 16; |
| |
| /* PR34054: use alternate RDHWR instruction encoding */ |
| if (((opcode & OPCODE) == BRDHWR && (opcode & FUNC) == RDHWR) |
| || ((opcode & OPCODE) == SPEC3 && (opcode & FUNC) == RDHWR)) { |
| |
| if (rd == 29) { |
| regs->regs[rt] = ti->tp_value; |
| atomic_inc(&brcm_rdhwr_count); |
| return 0; |
| } |
| } |
| |
| /* emulate MFC0 $15 for optimized memcpy() CPU detection */ |
| if ((opcode & OPCODE) == OP_MFC0 && |
| (opcode & OFFSET) == (15 << 11)) { |
| regs->regs[rt] = read_c0_prid(); |
| return 0; |
| } |
| |
| return -1; /* unhandled */ |
| } |
| |
| int brcm_unaligned_fp(void __user *addr, union mips_instruction *insn, |
| struct pt_regs *regs) |
| { |
| unsigned int op = insn->i_format.opcode; |
| unsigned int rt = insn->i_format.rt; |
| unsigned int res; |
| int wordlen = 8; |
| |
| /* on r4k, only the even slots ($f0, $f2, ...) are used */ |
| u8 *fprptr = (u8 *)current + THREAD_FPR0 + (rt >> 1) * |
| (THREAD_FPR2 - THREAD_FPR0); |
| |
| if (op == lwc1_op || op == swc1_op) { |
| wordlen = 4; |
| #ifdef __LITTLE_ENDIAN |
| /* LE: LSW ($f0) precedes MSW ($f1) */ |
| fprptr += (rt & 1) ? 4 : 0; |
| #else |
| /* BE: MSW ($f1) precedes LSW ($f0) */ |
| fprptr += (rt & 1) ? 0 : 4; |
| #endif |
| } |
| |
| preempt_disable(); |
| if (is_fpu_owner()) |
| save_fp(current); |
| else |
| own_fpu(1); |
| |
| if (op == lwc1_op || op == ldc1_op) { |
| if (!access_ok(VERIFY_READ, addr, wordlen)) |
| goto sigbus; |
| /* |
| * FPR load: copy from user struct to kernel saved |
| * register struct, then restore all FPRs |
| */ |
| __asm__ __volatile__ ( |
| "1: lb %0, 0(%3)\n" |
| " sb %0, 0(%2)\n" |
| " addiu %2, 1\n" |
| " addiu %3, 1\n" |
| " addiu %1, -1\n" |
| " bnez %1, 1b\n" |
| " li %0, 0\n" |
| "3:\n" |
| " .section .fixup,\"ax\"\n" |
| "4: li %0, %4\n" |
| " j 3b\n" |
| " .previous\n" |
| " .section __ex_table,\"a\"\n" |
| STR(PTR)" 1b,4b\n" |
| " .previous\n" |
| : "=&r" (res), "+r" (wordlen), |
| "+r" (fprptr), "+r" (addr) |
| : "i" (-EFAULT)); |
| if (res) |
| goto fault; |
| |
| restore_fp(current); |
| } else { |
| if (!access_ok(VERIFY_WRITE, addr, wordlen)) |
| goto sigbus; |
| /* |
| * FPR store: copy from kernel saved register struct |
| * to user struct |
| */ |
| __asm__ __volatile__ ( |
| "2: lb %0, 0(%2)\n" |
| "1: sb %0, 0(%3)\n" |
| " addiu %2, 1\n" |
| " addiu %3, 1\n" |
| " addiu %1, -1\n" |
| " bnez %1, 2b\n" |
| " li %0, 0\n" |
| "3:\n" |
| " .section .fixup,\"ax\"\n" |
| "4: li %0, %4\n" |
| " j 3b\n" |
| " .previous\n" |
| " .section __ex_table,\"a\"\n" |
| STR(PTR)" 1b,4b\n" |
| " .previous\n" |
| : "=&r" (res), "+r" (wordlen), |
| "+r" (fprptr), "+r" (addr) |
| : "i" (-EFAULT)); |
| if (res) |
| goto fault; |
| } |
| preempt_enable(); |
| |
| atomic_inc(&brcm_unaligned_fp_count); |
| return 0; |
| |
| sigbus: |
| preempt_enable(); |
| return -EINVAL; |
| |
| fault: |
| preempt_enable(); |
| return -EFAULT; |
| } |