blob: e1013d93c7658e53b095726c2183a59d7cf4391e [file] [log] [blame]
/*
* Copyright (C) 2009 - 2012 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/brcmstb/brcmstb.h>
#include "../drivers/mmc/host/sdhci.h"
#include "../drivers/mmc/host/sdhci-pltfm.h"
/* chip features */
int brcm_sata_enabled;
int brcm_pcie_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);
/* system / device settings */
unsigned long brcm_dram0_size_mb;
unsigned long brcm_dram1_size_mb;
unsigned long brcm_dram1_linux_mb;
unsigned long brcm_dram1_start = MEMC1_START;
unsigned long brcm_min_auth_region_size = 0x1000;
unsigned char brcm_eth0_phy[CFE_STRING_SIZE];
unsigned long brcm_eth0_speed;
unsigned long brcm_eth0_no_mdio;
unsigned char brcm_eth0_phyaddr[CFE_STRING_SIZE];
u8 brcm_eth0_macaddr[IFHWADDRLEN] = { 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef };
u8 brcm_moca0_macaddr[IFHWADDRLEN] = { 0x00, 0x00, 0xde, 0xad, 0xbe, 0xf0 };
unsigned long brcm_base_baud0 = BRCM_BASE_BAUD_STB; /* UPG UARTA */
unsigned long brcm_base_baud = BRCM_BASE_BAUD_STB; /* UPG_UART[BC] */
struct bcmemac_platform_data genet_pdata[BRCM_MAX_GENET];
/***********************************************************************
* Per-chip operations
***********************************************************************/
#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_BCM7231)
MAIN_CHIP_ID(7231, b0);
#elif defined(CONFIG_BCM7344)
MAIN_CHIP_ID(7344, b0);
#elif defined(CONFIG_BCM7346)
MAIN_CHIP_ID(7346, b0);
#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_BCM7360)
MAIN_CHIP_ID(7360, a0);
#elif defined(CONFIG_BCM7425)
MAIN_CHIP_ID(7425, b0);
#elif defined(CONFIG_BCM7429)
MAIN_CHIP_ID(7429, a0);
#elif defined(CONFIG_BCM7435)
MAIN_CHIP_ID(7435, a0);
#elif defined(CONFIG_BCM7552)
MAIN_CHIP_ID(7552, b0);
#elif defined(CONFIG_BCM7563)
MAIN_CHIP_ID(7563, a0);
#elif defined(CONFIG_BCM7584)
MAIN_CHIP_ID(7584, 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);
}
/***********************************************************************
* Common operations for all chips
***********************************************************************/
#ifdef CONFIG_BRCM_HAS_SATA3
#ifdef __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 /* __BIG_ENDIAN */
/* SATA3 SSC per-port bitfield */
static u32 sata3_enable_ssc = ~0;
#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 __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_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
}
#endif
void __init bchip_set_features(void)
{
#if defined(CONFIG_BRCM_HAS_SATA)
brcm_sata_enabled = 1;
#endif
#if defined(CONFIG_BRCM_HAS_PCIE)
brcm_pcie_enabled = 1;
#endif
#if defined(CONFIG_SMP) && defined(CONFIG_MIPS)
bmips_smp_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 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_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
#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
/*
* Initial GENET defaults
* These can be overridden by board_pinmux_setup() or by CFE vars
*/
#if defined(CONFIG_BRCM_HAS_GENET_0)
genet_pdata[0].base_reg = BCHP_GENET_0_SYS_REG_START;
genet_pdata[0].irq0 = BRCM_IRQ_GENET_0_A;
genet_pdata[0].irq1 = BRCM_IRQ_GENET_0_B;
#if defined(CONFIG_BRCM_MOCA_ON_GENET_0)
if (brcm_moca_enabled) {
genet_pdata[0].phy_type = BRCM_PHY_TYPE_MOCA;
genet_pdata[0].phy_id = BRCM_PHY_ID_NONE;
} else {
genet_pdata[0].phy_type = BRCM_PHY_TYPE_EXT_RGMII;
genet_pdata[0].phy_id = BRCM_PHY_ID_AUTO;
}
#else
genet_pdata[0].phy_type = BRCM_PHY_TYPE_INT;
genet_pdata[0].phy_id = 1;
#endif
#endif
#if defined(CONFIG_BRCM_HAS_GENET_1)
genet_pdata[1].base_reg = BCHP_GENET_1_SYS_REG_START;
genet_pdata[1].irq0 = BRCM_IRQ_GENET_1_A;
genet_pdata[1].irq1 = BRCM_IRQ_GENET_1_B;
genet_pdata[1].phy_type = BRCM_PHY_TYPE_EXT_RGMII;
genet_pdata[1].phy_id = BRCM_PHY_ID_AUTO;
#if defined(CONFIG_BRCM_MOCA_ON_GENET_1)
if (brcm_moca_enabled) {
genet_pdata[1].phy_type = BRCM_PHY_TYPE_MOCA;
genet_pdata[1].phy_id = BRCM_PHY_ID_NONE;
}
#endif
#endif
}
void macaddr_increment(u8 *buf, u8 len, u8 incr)
{
u8 old;
if (incr == 0)
return;
old = buf[len - 1];
buf[len - 1] += incr;
if (buf[len - 1] < old) {
buf[len - 2] += 1;
if (buf[len - 2] == 0) {
buf[len - 3] += 1;
}
}
}
EXPORT_SYMBOL(macaddr_increment);
int brcm_alloc_macaddr(u8 *buf, u8 intf_id, bool intf_is_moca)
{
if (intf_is_moca) {
/*
* Only one MoCA interface is supported.
* We can ignore intf_id offset.
*/
memcpy(buf, brcm_moca0_macaddr, ETH_ALEN);
} else {
memcpy(buf, brcm_eth0_macaddr, ETH_ALEN);
macaddr_increment(buf, ETH_ALEN, intf_id);
}
return 0;
}
EXPORT_SYMBOL(brcm_alloc_macaddr);
/***********************************************************************
* WKTMR utility functions (boot time only)
***********************************************************************/
void wktmr_read(struct wktmr_time *t)
{
uint32_t tmp;
do {
t->sec = BDEV_RD(BCHP_WKTMR_COUNTER);
tmp = BDEV_RD(BCHP_WKTMR_PRESCALER_VAL);
} while (tmp >= WKTMR_FREQ);
t->pre = WKTMR_FREQ - tmp;
}
unsigned long wktmr_elapsed(struct wktmr_time *t)
{
struct wktmr_time now;
wktmr_read(&now);
now.sec -= t->sec;
if (now.pre > t->pre) {
now.pre -= t->pre;
} else {
now.pre = WKTMR_FREQ + now.pre - t->pre;
now.sec--;
}
return (now.sec * WKTMR_FREQ) + now.pre;
}