blob: bec1e1a3f22980ad57e5b795bf94835459527e7a [file] [log] [blame]
/*
* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only 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.
*/
#include <common.h>
#include <asm/global_data.h>
#include <asm/io.h>
#include <asm-generic/errno.h>
#include <environment.h>
#include <fdtdec.h>
#include <asm/arch-qca-common/gsbi.h>
#include <asm/arch-qca-common/uart.h>
#include <asm/arch-qca-common/gpio.h>
#include <asm/arch-qca-common/smem.h>
#include <asm/arch-ipq806x/msm_ipq806x_gmac.h>
#include <linux/mtd/ipq_nand.h>
#include <asm/arch-qca-common/nand.h>
#include <asm/arch-ipq806x/clk.h>
#include <linux/usb/ipq_usb30.h>
#include <linux/usb/dwc3.h>
#include "ipq806x.h"
#include <asm/arch-qca-common/qca_common.h>
#include <asm/arch-qca-common/scm.h>
#include <asm/arch-qca-common/iomap.h>
#include <asm/io.h>
#include <dm/device.h>
#include <mmc.h>
#include <spi.h>
#include "ipq_spi.h"
#define DLOAD_MAGIC_COOKIE_1 0xE47B337D
#define DLOAD_MAGIC_COOKIE_2 0x0501CAB0
ipq_gmac_board_cfg_t gmac_cfg[CONFIG_IPQ_NO_MACS];
DECLARE_GLOBAL_DATA_PTR;
qca_mmc mmc_host;
const char *rsvd_node = "/reserved-memory";
const char *del_node[] = {NULL};
const add_node_t add_fdt_node[] = {{}};
struct dumpinfo_t dumpinfo_n[] = {
/* Note1: when aligned access is set, the contents
* are copied to a temporary location and so
* the size of region should not exceed the size
* of region pointed by IPQ_TEMP_DUMP_ADDR
*
* Note2: IPQ_NSSTCM_DUMP_ADDR should be the
* first entry */
{ "NSSTCM.BIN", IPQ_NSSTCM_DUMP_ADDR, 0x20000, 0 },
{ "IMEM_A.BIN", 0x2a000000, 0x0003f000, 0 },
{ "IMEM_C.BIN", 0x2a03f000, 0x00001000, 0 },
{ "IMEM_D.BIN", 0x2A040000, 0x00020000, 0 },
{ "CODERAM.BIN", 0x00020000, 0x00028000, 0 },
{ "SPS_RAM.BIN", 0x12000000, 0x0002C000, 0 },
{ "RPM_MSG.BIN", 0x00108000, 0x00005fff, 1 },
{ "SPS_BUFF.BIN", 0x12040000, 0x00004000, 0 },
{ "SPS_PIPE.BIN", 0x12800000, 0x00008000, 0 },
{ "LPASS.BIN", 0x28400000, 0x00020000, 0 },
{ "RPM_WDT.BIN", 0x0006206C, 0x00000004, 0 },
{ "CPU0_WDT.BIN", 0x0208A044, 0x00000004, 0 },
{ "CPU1_WDT.BIN", 0x0209A044, 0x00000004, 0 },
{ "CPU0_REG.BIN", 0x39013ea8, 0x000000AC, 0 },
{ "CPU1_REG.BIN", 0x39013f54, 0x000000AC, 0 },
{ "WLAN_FW.BIN", 0x41400000, 0x000FFF80, 0 },
{ "WLAN_FW_900B.BIN", 0x44000000, 0x00600000, 0 },
{ "EBICS0.BIN", 0x40000000, 0x20000000, 0 },
{ "EBI1CS1.BIN", 0x60000000, 0x20000000, 0 },
{ "EBICS2.BIN", 0x60000000, 0x20000000, 0, 0, 0, 0, 1 },
{ "EBICS1.BIN", CONFIG_UBOOT_END_ADDR, 0x10000000, 0, 0, 0, 0, 1 },
{ "EBICS0.BIN", 0x40000000, CONFIG_QCA_UBOOT_OFFSET, 0, 0, 0, 0, 1 }
};
int dump_entries_n = ARRAY_SIZE(dumpinfo_n);
struct dumpinfo_t dumpinfo_s[] = {
/* Note1: when aligned access is set, the contents
* are copied to a temporary location and so
* the size of region should not exceed the size
* of region pointed by IPQ_TEMP_DUMP_ADDR
*
* Note2: IPQ_NSSTCM_DUMP_ADDR should be the
* first entry */
{ "NSSTCM.BIN", IPQ_NSSTCM_DUMP_ADDR, 0x20000, 0 },
{ "IMEM_A.BIN", 0x2a000000, 0x0003f000, 0 },
{ "IMEM_C.BIN", 0x2a03f000, 0x00001000, 0 },
{ "IMEM_D.BIN", 0x2A040000, 0x00020000, 0 },
{ "CODERAM.BIN", 0x00020000, 0x00028000, 0 },
{ "SPS_RAM.BIN", 0x12000000, 0x0002C000, 0 },
{ "RPM_MSG.BIN", 0x00108000, 0x00005fff, 1 },
{ "SPS_BUFF.BIN", 0x12040000, 0x00004000, 0 },
{ "SPS_PIPE.BIN", 0x12800000, 0x00008000, 0 },
{ "LPASS.BIN", 0x28400000, 0x00020000, 0 },
{ "RPM_WDT.BIN", 0x0006206C, 0x00000004, 0 },
{ "CPU0_WDT.BIN", 0x0208A044, 0x00000004, 0 },
{ "CPU1_WDT.BIN", 0x0209A044, 0x00000004, 0 },
{ "CPU0_REG.BIN", 0x39013ea8, 0x000000AC, 0 },
{ "CPU1_REG.BIN", 0x39013f54, 0x000000AC, 0 },
{ "WLAN_FW.BIN", 0x41400000, 0x000FFF80, 0 },
{ "WLAN_FW_900B.BIN", 0x44000000, 0x00600000, 0 },
{ "EBICS0.BIN", 0x40000000, 0x20000000, 0 },
{ "EBI1CS1.BIN", 0x60000000, 0x20000000, 0 },
{ "EBICS2.BIN", 0x60000000, 0x20000000, 0, 0, 0, 0, 1 },
{ "EBICS1.BIN", CONFIG_UBOOT_END_ADDR, 0x10000000, 0, 0, 0, 0, 1 },
{ "EBICS0.BIN", 0x40000000, CONFIG_QCA_UBOOT_OFFSET, 0, 0, 0, 0, 1 }
};
int dump_entries_s = ARRAY_SIZE(dumpinfo_s);
extern int ipq_spi_init(u16);
pci_clk_offset_t pcie_0_clk = {
.aclk_ctl = PCIE_0_ACLK_CTL,
.pclk_ctl = PCIE_0_PCLK_CTL,
.hclk_ctl = PCIE_0_HCLK_CTL,
.aux_clk_ctl = PCIE_0_AUX_CLK_CTL,
.alt_ref_clk_ns = PCIE_0_ALT_REF_CLK_NS,
.alt_ref_clk_acr = PCIE_0_ALT_REF_CLK_ACR,
.aclk_fs = PCIE_0_ACLK_FS,
.pclk_fs = PCIE_0_PCLK_FS,
.parf_phy_refclk = PCIE20_0_PARF_PHY_REFCLK
};
pci_clk_offset_t pcie_1_clk = {
.aclk_ctl = PCIE_1_ACLK_CTL,
.pclk_ctl = PCIE_1_PCLK_CTL,
.hclk_ctl = PCIE_1_HCLK_CTL,
.aux_clk_ctl = PCIE_1_AUX_CLK_CTL,
.alt_ref_clk_ns = PCIE_1_ALT_REF_CLK_NS,
.alt_ref_clk_acr = PCIE_1_ALT_REF_CLK_ACR,
.aclk_fs = PCIE_1_ACLK_FS,
.pclk_fs = PCIE_1_PCLK_FS,
.parf_phy_refclk = PCIE20_1_PARF_PHY_REFCLK
};
pci_clk_offset_t pcie_2_clk = {
.aclk_ctl = PCIE_2_ACLK_CTL,
.pclk_ctl = PCIE_2_PCLK_CTL,
.hclk_ctl = PCIE_2_HCLK_CTL,
.aux_clk_ctl = PCIE_2_AUX_CLK_CTL,
.alt_ref_clk_ns = PCIE_2_ALT_REF_CLK_NS,
.alt_ref_clk_acr = PCIE_2_ALT_REF_CLK_ACR,
.aclk_fs = PCIE_2_ACLK_FS,
.pclk_fs = PCIE_2_PCLK_FS,
.parf_phy_refclk = PCIE20_2_PARF_PHY_REFCLK
};
enum pcie_id {
PCIE_0,
PCIE_1,
PCIE_2,
};
unsigned long timer_read_counter(void)
{
return 0;
}
void reset_crashdump(void)
{
unsigned long *dmagic1 = (unsigned long *) 0x2A03F000;
unsigned long *dmagic2 = (unsigned long *) 0x2A03F004;
*dmagic1 = 0;
*dmagic2 = 0;
return;
}
void reset_cpu(unsigned long a)
{
int reset_s17_gpio_node;
reset_s17_gpio_node = fdt_path_offset(gd->fdt_blob, "/reset_s17_gpio");
if (reset_s17_gpio_node)
qca_gpio_init(reset_s17_gpio_node);
reset_crashdump();
printf("\nResetting with watch dog!\n");
writel(0, APCS_WDT0_EN);
writel(1, APCS_WDT0_RST);
writel(RESET_WDT_BARK_TIME, APCS_WDT0_BARK_TIME);
writel(RESET_WDT_BITE_TIME, APCS_WDT0_BITE_TIME);
writel(1, APCS_WDT0_EN);
writel(1, APCS_WDT0_CPU0_WDOG_EXPIRED_ENABLE);
while(1);
}
void reset_board(void)
{
run_command("reset", 0);
}
void ipq_uboot_fdt_fixup(void)
{
int ret, len;
const char *config = "config@ap148_1xx";
len = fdt_totalsize(gd->fdt_blob) + strlen(config) + 1;
if (gd->bd->bi_arch_number == MACH_TYPE_IPQ806X_AP148_1XX)
{
/*
* Open in place with a new length.
*/
ret = fdt_open_into(gd->fdt_blob, (void *)gd->fdt_blob, len);
if (ret)
debug("uboot-fdt-fixup: Cannot expand FDT: %s\n", fdt_strerror(ret));
ret = fdt_setprop((void *)gd->fdt_blob, 0, "config_name",
config, (strlen(config)+1));
if (ret)
debug("uboot-fdt-fixup: unable to set config_name(%d)\n", ret);
}
return;
}
int board_mmc_init(bd_t *bis)
{
int node, gpio_node;
int ret = -ENODEV;
const u32 *emmc_base;
int len;
qca_smem_flash_info_t *sfi = &qca_smem_flash_info;
node = fdt_path_offset(gd->fdt_blob, "sdcc");
if (node < 0) {
printf("SDCC : Node Not found, skipping initialization\n");
goto out;
}
emmc_base = fdt_getprop(gd->fdt_blob, node, "reg", &len);
if ((u32)emmc_base == FDT_ADDR_T_NONE) {
printf("No valid SDCC base address found in device tree\n");
goto out;
}
gpio_node = fdt_subnode_offset(gd->fdt_blob, node, "emmc_gpio");
if (gpio_node >= 0) {
mmc_host.clk_mode = MMC_IDENTIFY_MODE;
mmc_host.base = fdt32_to_cpu(emmc_base[0]);
emmc_clock_config(mmc_host.clk_mode);
qca_gpio_init(gpio_node);
ret = qca_mmc_init(bis, &mmc_host);
}
if (!ret && sfi->flash_type == SMEM_BOOT_MMC_FLASH) {
ret = board_mmc_env_init(mmc_host);
}
out:
return ret;
}
void board_nand_init(void)
{
int node, gpio_node;
const u32 *nand_base;
struct ipq_nand ipq_nand;
int len;
node = fdt_path_offset(gd->fdt_blob, "nand");
if (node < 0) {
printf("NAND : Not found, skipping initialization\n");
goto spi_init;
}
nand_base = fdt_getprop(gd->fdt_blob, node, "reg", &len);
if ((u32)nand_base == FDT_ADDR_T_NONE) {
printf("No valid NAND base address found in device tree\n");
goto spi_init;
}
gpio_node = fdt_subnode_offset(gd->fdt_blob, node, "nand_gpio");
if (gpio_node >= 0) {
nand_clock_config();
memset(&ipq_nand, 0, sizeof(ipq_nand));
ipq_nand.ebi2cr_regs = fdt32_to_cpu(nand_base[0]);
ipq_nand.ebi2nd_regs = fdt32_to_cpu(nand_base[2]);
ipq_nand.layout = IPQ_NAND_LAYOUT_LINUX;
ipq_nand.variant = QCA_NAND_IPQ;
qca_gpio_init(gpio_node);
ipq_nand_init(&ipq_nand);
}
spi_init:
if(!(gsbi_pin_config(CONFIG_SF_DEFAULT_BUS, CONFIG_SF_DEFAULT_CS)))
ipq_spi_init(CONFIG_SPI_FLASH_INFO_IDX);
}
int board_eth_init(bd_t *bis)
{
int status;
int gmac_gpio_node = 0, storm_switch_gpio_node = 0;
int ak01_reset_gpio_node = 0, ak01_config_gpio_node = 0;
int gmac_cfg_node = 0, offset = 0;
unsigned int machid;
int loop = 0, inner_loop = 0;
int phy_name_len = 0;
unsigned int tmp_phy_array[8] = {0};
char *phy_name_ptr = NULL;
gmac_cfg_node = fdt_path_offset(gd->fdt_blob, "/gmac_cfg");
if (gmac_cfg_node >= 0) {
for (offset = fdt_first_subnode(gd->fdt_blob, gmac_cfg_node);
offset > 0;
offset = fdt_next_subnode(gd->fdt_blob, offset) , loop++) {
gmac_cfg[loop].base = fdtdec_get_uint(gd->fdt_blob,
offset, "base", 0);
gmac_cfg[loop].unit = fdtdec_get_uint(gd->fdt_blob,
offset, "unit", 0);
gmac_cfg[loop].is_macsec = fdtdec_get_uint(gd->fdt_blob,
offset, "is_macsec", 0);
gmac_cfg[loop].mac_pwr0 = fdtdec_get_uint(gd->fdt_blob,
offset, "mac_pwr0", 0);
gmac_cfg[loop].mac_pwr1 = fdtdec_get_uint(gd->fdt_blob,
offset, "mac_pwr1", 0);
gmac_cfg[loop].mac_conn_to_phy = fdtdec_get_uint(gd->fdt_blob,
offset, "mac_conn_to_phy", 0);
gmac_cfg[loop].phy = fdtdec_get_uint(gd->fdt_blob,
offset, "phy_interface_type", 0);
gmac_cfg[loop].phy_addr.count = fdtdec_get_uint(gd->fdt_blob,
offset, "phy_address_count", 0);
fdtdec_get_int_array(gd->fdt_blob, offset, "phy_address",
tmp_phy_array, gmac_cfg[loop].phy_addr.count);
for(inner_loop = 0; inner_loop < gmac_cfg[loop].phy_addr.count;
inner_loop++){
gmac_cfg[loop].phy_addr.addr[inner_loop] =
(char)tmp_phy_array[inner_loop];
}
phy_name_ptr = (char*)fdt_getprop(gd->fdt_blob, offset,
"phy_name", &phy_name_len);
strncpy((char *)gmac_cfg[loop].phy_name, phy_name_ptr, phy_name_len);
}
}
gmac_cfg[loop].unit = -1;
storm_switch_gpio_node = fdt_path_offset(gd->fdt_blob,
"/storm_switch_gpio");
if (storm_switch_gpio_node) {
qca_gpio_init(storm_switch_gpio_node);
}
ipq_gmac_common_init(gmac_cfg);
gmac_gpio_node = fdt_path_offset(gd->fdt_blob, "gmac_gpio");
if (gmac_gpio_node) {
qca_gpio_init(gmac_gpio_node);
}
/*
* Register the swith driver routines before
* initializng the GMAC
*/
machid = fdtdec_get_uint(gd->fdt_blob, 0, "machid", 0);
switch (machid) {
case MACH_TYPE_IPQ806X_AP160_2XX:
ipq_register_switch(ipq_qca8511_init);
break;
case MACH_TYPE_IPQ806X_AK01_1XX:
ak01_reset_gpio_node = fdt_path_offset(gd->fdt_blob, "/ak01_gmac_reset_gpio");
if (ak01_reset_gpio_node){
qca_gpio_init(ak01_reset_gpio_node);
}
mdelay(100);
ak01_config_gpio_node = fdt_path_offset(gd->fdt_blob, "/ak01_gmac_config_gpio");
if (ak01_config_gpio_node){
qca_gpio_init(ak01_config_gpio_node);
};
ipq_register_switch(NULL);
break;
default:
ipq_register_switch(ipq_athrs17_init);
break;
}
status = ipq_gmac_init(gmac_cfg);
return status;
}
void qca_serial_init(struct ipq_serial_platdata *plat)
{
int serial_node, gpio_node, uart2_node;
unsigned gsbi_base;
serial_node = fdt_path_offset(gd->fdt_blob, "console");
if (serial_node < 0) {
return;
}
if (plat->port_id == 2) {
uart2_node = fdt_path_offset(gd->fdt_blob, "uart2");
if (uart2_node < 0) {
printf("uart2 node not defined\n");
} else {
serial_node = uart2_node;
}
}
gpio_node = fdt_subnode_offset(gd->fdt_blob,
serial_node, "serial_gpio");
gsbi_base = fdtdec_get_uint(gd->fdt_blob,
serial_node, "gsbi_base", 0);
if (!gsbi_base)
return;
qca_gpio_init(gpio_node);
if (!(plat->m_value == -1) || ( plat->n_value == -1) || (plat->d_value == -1))
uart_clock_config(plat->port_id,
plat->m_value,
plat->n_value,
plat->d_value);
writel(GSBI_PROTOCOL_CODE_I2C_UART <<
GSBI_CTRL_REG_PROTOCOL_CODE_S,
GSBI_CTRL_REG(gsbi_base));
}
void ipq_wifi_pci_power_enable(void)
{
int offset;
u32 gpio;
offset = fdt_path_offset(gd->fdt_blob, "pci_pwr");
if (offset >= 0) {
qca_gpio_init(offset);
for (offset = fdt_first_subnode(gd->fdt_blob, offset); offset > 0;
offset = fdt_next_subnode(gd->fdt_blob, offset)) {
gpio = fdtdec_get_uint(gd->fdt_blob,
offset, "gpio", 0);
gpio_set_value(gpio, 1);
}
}
}
static void ipq_wifi_pci_power_disable(void)
{
int offset;
u32 gpio;
offset = fdt_path_offset(gd->fdt_blob, "pci_pwr");
if (offset >= 0) {
for (offset = fdt_first_subnode(gd->fdt_blob, offset); offset > 0;
offset = fdt_next_subnode(gd->fdt_blob, offset)) {
gpio = fdtdec_get_uint(gd->fdt_blob,
offset, "gpio", 0);
gpio_set_value(gpio, 0);
}
}
}
void board_pcie_clock_init(int id)
{
switch(id) {
case PCIE_0:
pcie_clock_config(&pcie_0_clk);
break;
case PCIE_1:
pcie_clock_config(&pcie_1_clk);
break;
case PCIE_2:
pcie_clock_config(&pcie_2_clk);
break;
}
}
void board_pci_init(int id)
{
int node, gpio_node, offset;
char name[16];
u32 gpio;
sprintf(name, "pci%d", id);
node = fdt_path_offset(gd->fdt_blob, name);
if (node < 0) {
printf("Could not find PCI in device tree\n");
return;
}
gpio_node = fdt_subnode_offset(gd->fdt_blob, node, "pci_gpio");
if (gpio_node >= 0) {
qca_gpio_init(gpio_node);
offset = fdt_first_subnode(gd->fdt_blob, gpio_node);
if (offset) {
gpio = fdtdec_get_uint(gd->fdt_blob,
offset, "gpio", 0);
gpio_set_value(gpio, 1);
udelay(3000);
}
}
return;
}
void board_pci_deinit()
{
int node, i;
char name[16];
struct fdt_resource parf;
struct fdt_resource pci_rst;
struct qca_gpio_config gpio_config = {0};
for (i = 0; i < PCI_MAX_DEVICES; i++) {
snprintf(name, sizeof(name), "pci%d", i);
node = fdt_path_offset(gd->fdt_blob, name);
if (node < 0) {
printf("Could not find PCI in device tree\n");
return;
}
gpio_config.gpio = fdtdec_get_uint(gd->fdt_blob,
node, "perst_gpio", 0);
gpio_tlmm_config(&gpio_config);
fdt_get_named_resource(gd->fdt_blob, node, "reg", "reg-names", "pci_rst",
&pci_rst);
writel(0x7d, pci_rst.start);
fdt_get_named_resource(gd->fdt_blob, node, "reg", "reg-names", "parf",
&parf);
writel(0x1, parf.start + 0x40);
switch(i) {
case PCIE_0:
pcie_clock_shutdown(&pcie_0_clk);
break;
case PCIE_1:
pcie_clock_shutdown(&pcie_1_clk);
break;
case PCIE_2:
pcie_clock_shutdown(&pcie_2_clk);
break;
}
}
ipq_wifi_pci_power_disable();
return ;
}
void ipq_fdt_fixup_socinfo(void *blob)
{
uint32_t cpu_type;
uint32_t soc_version, soc_version_major, soc_version_minor;
int nodeoff, ret;
nodeoff = fdt_path_offset(blob, "/");
if (nodeoff < 0) {
printf("ipq: fdt fixup cannot find root node\n");
return;
}
ret = ipq_smem_get_socinfo_cpu_type(&cpu_type);
if (ret) {
return;
}
/* Add "cpu_type" to root node of the devicetree*/
ret = ipq_smem_get_socinfo_cpu_type(&cpu_type);
if (!ret) {
ret = fdt_setprop(blob, nodeoff, "cpu_type",
&cpu_type, sizeof(cpu_type));
if (ret)
printf("%s: cannot set cpu type %d\n", __func__, ret);
} else {
printf("ipq: fdt fixup cannot get socinfo\n");
}
ret = ipq_smem_get_socinfo_version((uint32_t *)&soc_version);
if (!ret) {
soc_version_major = SOCINFO_VERSION_MAJOR(soc_version);
soc_version_minor = SOCINFO_VERSION_MINOR(soc_version);
ret = fdt_setprop(blob, nodeoff, "soc_version_major",
&soc_version_major,
sizeof(soc_version_major));
if (ret)
printf("%s: cannot set soc_version_major %d\n",
__func__, soc_version_major);
ret = fdt_setprop(blob, nodeoff, "soc_version_minor",
&soc_version_minor,
sizeof(soc_version_minor));
if (ret)
printf("%s: cannot set soc_version_minor %d\n",
__func__, soc_version_minor);
} else {
printf("%s: cannot get soc version\n", __func__);
}
}
void ipq_fdt_fixup_usb_device_mode(void *blob)
{
return;
}
void fdt_fixup_auto_restart(void *blob)
{
return;
}
void board_mmc_deinit(void)
{
emmc_clock_reset();
emmc_clock_disable();
}
void set_flash_secondary_type(qca_smem_flash_info_t *smem)
{
/*
* Both eMMC and NAND share common GPIOs, only one of them shall be
* enabled from device tree, based on board configuration.
*
* flash_secondary_type is set to eMMC/NAND device whichever is
* initialized, as there is no smem entry to differentiate between the
* two.
*/
#ifdef CONFIG_QCA_MMC
struct mmc *mmc;
mmc = find_mmc_device(mmc_host.dev_num);
if (mmc) {
smem->flash_secondary_type = SMEM_BOOT_MMC_FLASH;
return;
}
#endif
smem->flash_secondary_type = SMEM_BOOT_NAND_FLASH;
return;
}
int switch_ce_channel_buf(unsigned int channel_id)
{
int ret;
switch_ce_chn_buf_t ce1_chn_buf;
ce1_chn_buf.resource = CE1_RESOURCE;
ce1_chn_buf.channel_id = channel_id;
ret = scm_call(SCM_SVC_TZ, CE_CHN_SWITCH_CMD, &ce1_chn_buf,
sizeof(switch_ce_chn_buf_t), NULL, 0);
return ret;
}
#ifdef CONFIG_USB_XHCI_IPQ
__weak void ipq_reset_usb_phy(void *data)
{
return;
}
static u16 dwc3_ipq_ssusb_read_phy_reg(unsigned int addr, unsigned int ipq_base)
{
u16 tmp_phy[3], i;
do {
for (i = 0; i < 3; i++) {
writel(addr, ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_DATA_IN);
writel(0x1, ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_CAP_ADDR);
while (0 != readl(ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_CAP_ADDR));
writel(0x1, ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_READ);
while (0 != readl(ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_READ));
tmp_phy[i] = (u16)readl(ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_DATA_OUT);
}
} while (tmp_phy[1] != tmp_phy[2]);
return tmp_phy[2];
}
static void dwc3_ipq_ssusb_write_phy_reg(u32 addr, u16 data, unsigned int ipq_base)
{
writel(addr, ipq_base + IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_DATA_IN);
writel(0x1, ipq_base + IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_CAP_ADDR);
while (0 != readl(ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_CAP_ADDR));
writel(data, ipq_base + IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_DATA_IN);
writel(0x1, ipq_base + IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_CAP_DATA);
while (0 != readl(ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_CAP_DATA));
writel(0x1, ipq_base + IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_WRITE);
while (0 != readl(ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_WRITE));
}
static void ipq_ssusb_clear_bits32(u32 offset, u32 bits, unsigned int ipq_base)
{
u32 data;
data = readl(ipq_base+offset);
data = data & ~bits;
writel(data, ipq_base + offset);
}
static void ipq_ssusb_clear_and_set_bits32(u32 offset, u32 clear_bits, u32 set_bits, unsigned int ipq_base)
{
u32 data;
data = readl(ipq_base + offset);
data = (data & ~clear_bits) | set_bits;
writel(data, ipq_base + offset);
}
static void partial_rx_reset_init(unsigned int ipq_base)
{
u32 addr = DWC3_SSUSB_PHY_TX_ALT_BLOCK_REG;
u16 data = dwc3_ipq_ssusb_read_phy_reg(addr, ipq_base);
data |= DWC3_SSUSB_PHY_TX_ALT_BLOCK_EN_ALT_BUS;
dwc3_ipq_ssusb_write_phy_reg(addr, data, ipq_base);
return;
}
static void uw_ssusb_pre_init(unsigned int ipq_base)
{
u32 set_bits, tmp;
/* GCTL Reset ON */
writel(0x800, ipq_base + DWC3_SSUSB_REG_GCTL);
/* Config SS PHY CTRL */
set_bits = 0;
writel(0x80, ipq_base + IPQ_SS_PHY_CTRL_REG);
udelay(5);
ipq_ssusb_clear_bits32(IPQ_SS_PHY_CTRL_REG, 0x80, ipq_base);
udelay(5);
/* REF_USE_PAD */
set_bits = 0x0000000; /* USE Internal clock */
set_bits |= IPQ_SSUSB_QSCRATCH_SS_PHY_CTRL_LANE0_PWR_PRESENT;
set_bits |= IPQ_SSUSB_QSCRATCH_SS_PHY_CTRL_REF_SS_PHY_EN;
writel(set_bits, ipq_base + IPQ_SS_PHY_CTRL_REG);
/* Config HS PHY CTRL */
set_bits = IPQ_SSUSB_REG_QSCRATCH_HS_PHY_CTRL_UTMI_OTG_VBUS_VALID;
/*
* COMMONONN forces xo, bias and pll to stay on during suspend;
* Allowing suspend (writing 1) kills Aragorn V1
*/
set_bits |= IPQ_SSUSB_REG_QSCRATCH_HS_PHY_CTRL_COMMONONN;
set_bits |= IPQ_SSUSB_REG_QSCRATCH_HS_PHY_CTRL_USE_CLKCORE;
set_bits |= IPQ_SSUSB_REG_QSCRATCH_HS_PHY_CTRL_FSEL_VAL;
/*
* If the configuration of clocks is not bypassed in Host mode,
* HS PHY suspend needs to be prohibited, otherwise - SS connection fails
*/
ipq_ssusb_clear_and_set_bits32(IPQ_SSUSB_REG_QSCRATCH_HS_PHY_CTRL, 0,
set_bits, ipq_base);
/* USB2 PHY Reset ON */
writel(DWC3_SSUSB_REG_GUSB2PHYCFG_PHYSOFTRST, ipq_base +
DWC3_SSUSB_REG_GUSB2PHYCFG(0));
/* USB3 PHY Reset ON */
writel(DWC3_SSUSB_REG_GUSB3PIPECTL_PHYSOFTRST, ipq_base +
DWC3_SSUSB_REG_GUSB3PIPECTL(0));
udelay(5);
/* USB3 PHY Reset OFF */
ipq_ssusb_clear_bits32(DWC3_SSUSB_REG_GUSB3PIPECTL(0),
DWC3_SSUSB_REG_GUSB3PIPECTL_PHYSOFTRST, ipq_base);
ipq_ssusb_clear_bits32(DWC3_SSUSB_REG_GUSB2PHYCFG(0),
DWC3_GUSB2PHYCFG_PHYSOFTRST, ipq_base);
udelay(5);
/* GCTL Reset OFF */
ipq_ssusb_clear_bits32(DWC3_SSUSB_REG_GCTL, DWC3_GCTL_CORESOFTRESET,
ipq_base);
udelay(5);
if (RX_TERM_VALUE) {
dwc3_ipq_ssusb_write_phy_reg(DWC3_SSUSB_PHY_RTUNE_RTUNE_CTRL_REG,
0, ipq_base);
dwc3_ipq_ssusb_write_phy_reg(DWC3_SSUSB_PHY_RTUNE_DEBUG_REG,
0x0448, ipq_base);
dwc3_ipq_ssusb_write_phy_reg(DWC3_SSUSB_PHY_RTUNE_DEBUG_REG,
RX_TERM_VALUE, ipq_base);
}
if (0 != RX_EQ_VALUE) { /* Values from 1 to 7 */
tmp =0;
/*
* 1. Fixed EQ setting. This can be achieved as follows:
* LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0 - address 1006 bit 6
* LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1 0- address 1006 bit 7
* LANE0.RX_OVRD_IN_HI.RX_EQ set to 4 (also try setting 3 if possible) -
* address 1006 bits 10:8 - please make this a variable, if unchanged the section is not executed
* LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1 - address 1006 bit 11
*/
tmp = dwc3_ipq_ssusb_read_phy_reg(DWC3_SSUSB_PHY_RX_OVRD_IN_HI_REG,
ipq_base);
tmp &= ~((u16)1 << DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ_EN);
tmp |= ((u16)1 << DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ_EN_OVRD);
tmp &= ~((u16) DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ_MASK <<
DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ);
tmp |= RX_EQ_VALUE << DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ;
tmp |= 1 << DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ_OVRD;
dwc3_ipq_ssusb_write_phy_reg(DWC3_SSUSB_PHY_RX_OVRD_IN_HI_REG,
tmp, ipq_base);
}
if ((113 != AMPLITUDE_VALUE) || (21 != TX_DEEMPH_3_5DB)) {
tmp = dwc3_ipq_ssusb_read_phy_reg(DWC3_SSUSB_PHY_TX_OVRD_DRV_LO_REG,
ipq_base);
tmp &= ~DWC3_SSUSB_PHY_TX_DEEMPH_MASK;
tmp |= (TX_DEEMPH_3_5DB << DWC3_SSUSB_PHY_TX_DEEMPH_SHIFT);
tmp &= ~DWC3_SSUSB_PHY_AMP_MASK;
tmp |= AMPLITUDE_VALUE;
tmp |= DWC3_SSUSB_PHY_AMP_EN;
dwc3_ipq_ssusb_write_phy_reg(DWC3_SSUSB_PHY_TX_OVRD_DRV_LO_REG,
tmp, ipq_base);
}
ipq_ssusb_clear_and_set_bits32(IPQ_SS_PHY_PARAM_CTRL_1_REG,
0x7, 0x5, ipq_base);
/* XHCI REV */
writel((1 << 2), ipq_base + IPQ_QSCRATCH_GENERAL_CFG);
writel(0x0c80c010, ipq_base + DWC3_SSUSB_REG_GUCTL);
partial_rx_reset_init(ipq_base);
set_bits = 0;
/* Test U2EXIT_LFPS */
set_bits |= IPQ_SSUSB_REG_GCTL_U2EXIT_LFPS;
ipq_ssusb_clear_and_set_bits32(DWC3_SSUSB_REG_GCTL, 0,
set_bits, ipq_base);
set_bits = 0;
set_bits |= IPQ_SSUSB_REG_GCTL_U2RSTECN;
set_bits |= IPQ_SSUSB_REG_GCTL_U2EXIT_LFPS;
ipq_ssusb_clear_and_set_bits32(DWC3_SSUSB_REG_GCTL, 0,
set_bits, ipq_base);
writel(DWC3_GCTL_U2EXIT_LFPS | DWC3_GCTL_SOFITPSYNC |
DWC3_GCTL_PRTCAPDIR(1) |
DWC3_GCTL_U2RSTECN | DWC3_GCTL_PWRDNSCALE(2),
ipq_base + DWC3_GCTL);
writel((IPQ_SSUSB_QSCRATCH_SS_PHY_CTRL_MPLL_MULTI(0x19) |
IPQ_SSUSB_QSCRATCH_SS_PHY_CTRL_REF_SS_PHY_EN |
IPQ_SSUSB_QSCRATCH_SS_PHY_CTRL_LANE0_PWR_PRESENT),
ipq_base + IPQ_SS_PHY_CTRL_REG);
writel((DWC3_SSUSB_REG_GUSB2PHYCFG_SUSPENDUSB20 |
DWC3_SSUSB_REG_GUSB2PHYCFG_ENBLSLPM |
DWC3_SSUSB_REG_GUSB2PHYCFG_USBTRDTIM(9)),
ipq_base + DWC3_SSUSB_REG_GUSB2PHYCFG(0));
writel(DWC3_SSUSB_REG_GUSB3PIPECTL_ELASTIC_BUFFER_MODE |
DWC3_SSUSB_REG_GUSB3PIPECTL_TX_DE_EPPHASIS(1) |
DWC3_SSUSB_REG_GUSB3PIPECTL_TX_MARGIN(0)|
DWC3_SSUSB_REG_GUSB3PIPECTL_DELAYP1TRANS |
DWC3_SSUSB_REG_GUSB3PIPECTL_DELAYP1P2P3(1) |
DWC3_SSUSB_REG_GUSB3PIPECTL_U1U2EXITFAIL_TO_RECOV |
DWC3_SSUSB_REG_GUSB3PIPECTL_REQUEST_P1P2P3,
ipq_base + DWC3_SSUSB_REG_GUSB3PIPECTL(0));
writel(IPQ_SSUSB_REG_QSCRATCH_SS_PHY_PARAM_CTRL_1_LOS_LEVEL(0x9) |
IPQ_SSUSB_REG_QSCRATCH_SS_PHY_PARAM_CTRL_1_TX_DEEMPH_3_5DB(0x17) |
IPQ_SSUSB_REG_QSCRATCH_SS_PHY_PARAM_CTRL_1_TX_DEEMPH_6DB(0x20) |
IPQ_SSUSB_REG_QSCRATCH_SS_PHY_PARAM_CTRL_1_TX_SWING_FULL(0x6E),
ipq_base + IPQ_SS_PHY_PARAM_CTRL_1_REG);
writel(IPQ_SSUSB_REG_QSCRATCH_GENERAL_CFG_XHCI_REV(DWC3_SSUSB_XHCI_REV_10),
ipq_base + IPQ_QSCRATCH_GENERAL_CFG);
}
static void usb30_common_pre_init(int id, unsigned int ipq_base)
{
unsigned int reg;
if (id == 0)
reg = USB30_RESET;
else
reg = USB30_1_RESET;
writel(IPQ_USB30_RESET_PHY_ASYNC_RESET, reg);
writel(IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET, reg);
writel(IPQ_USB30_RESET_MOC_UTMI_ASYNC_RESET |
IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET, reg);
writel(IPQ_USB30_RESET_SLEEP_ASYNC_RESET |
IPQ_USB30_RESET_MOC_UTMI_ASYNC_RESET |
IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET, reg);
writel(IPQ_USB30_RESET_MASTER_ASYNC_RESET |
IPQ_USB30_RESET_SLEEP_ASYNC_RESET |
IPQ_USB30_RESET_MOC_UTMI_ASYNC_RESET |
IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET, reg);
if (id == 0) {
writel(IPQ_USB30_RESET_PORT2_HS_PHY_ASYNC_RESET |
IPQ_USB30_RESET_MASTER_ASYNC_RESET |
IPQ_USB30_RESET_SLEEP_ASYNC_RESET |
IPQ_USB30_RESET_MOC_UTMI_ASYNC_RESET |
IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET, reg);
}
udelay(5);
writel(IPQ_USB30_RESET_MASK & ~(IPQ_USB30_RESET_PHY_ASYNC_RESET), reg);
writel(IPQ_USB30_RESET_MASK & ~(IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET), reg);
writel(IPQ_USB30_RESET_MASK & ~(IPQ_USB30_RESET_MOC_UTMI_ASYNC_RESET |
IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET), reg);
writel(IPQ_USB30_RESET_MASK & ~(IPQ_USB30_RESET_SLEEP_ASYNC_RESET |
IPQ_USB30_RESET_MOC_UTMI_ASYNC_RESET |
IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET), reg);
writel(IPQ_USB30_RESET_MASK & ~(IPQ_USB30_RESET_MASTER_ASYNC_RESET |
IPQ_USB30_RESET_SLEEP_ASYNC_RESET|
IPQ_USB30_RESET_MOC_UTMI_ASYNC_RESET |
IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET), reg);
if (id == 0) {
writel(IPQ_USB30_RESET_MASK &
~(IPQ_USB30_RESET_PORT2_HS_PHY_ASYNC_RESET |
IPQ_USB30_RESET_MASTER_ASYNC_RESET |
IPQ_USB30_RESET_SLEEP_ASYNC_RESET |
IPQ_USB30_RESET_MOC_UTMI_ASYNC_RESET |
IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET), reg);
reg = IPQ_TCSR_USB_CONTROLLER_TYPE_SEL;
if (reg) {
qca_scm_usb_mode_write(reg, 0X3);
}
}
writel((IPQ_SSUSB_REG_QSCRATCH_CGCTL_RAM1112_EN |
IPQ_SSUSB_REG_QSCRATCH_CGCTL_RAM13_EN),
ipq_base + IPQ_SSUSB_REG_QSCRATCH_CGCTL);
writel((IPQ_SSUSB_REG_QSCRATCH_RAM1_RAM13_EN |
IPQ_SSUSB_REG_QSCRATCH_RAM1RAM12_EN |
IPQ_SSUSB_REG_QSCRATCH_RAM1_RAM11_EN),
ipq_base + IPQ_SSUSB_REG_QSCRATCH_RAM1);
}
int ipq_board_usb_init(void)
{
int i;
unsigned int ipq_base;
/* Configure the usb core clock */
usb_ss_core_clock_config(0, 1, 5, 32);
/* Configure the usb core clock */
usb_ss_utmi_clock_config(0, 1, 40, 1);
for (i = 0; i < CONFIG_USB_MAX_CONTROLLER_COUNT; i++) {
if (i == 0)
ipq_base = IPQ_XHCI_BASE_1;
else
ipq_base = IPQ_XHCI_BASE_2;
usb30_common_pre_init(0, ipq_base);
uw_ssusb_pre_init(ipq_base);
}
return 0;
}
#endif /* CONFIG_USB_XHCI_IPQ */
int apps_iscrashed(void)
{
u32 *dmagic1 = (u32 *)0x2A03F000;
u32 *dmagic2 = (u32 *)0x2A03F004;
if (*dmagic1 == DLOAD_MAGIC_COOKIE_1
&& *dmagic2 == DLOAD_MAGIC_COOKIE_2)
return 1;
return 0;
}
int ipq_get_tz_version(char *version_name, int buf_size)
{
int ret;
ret = scm_call(SCM_SVC_INFO, TZBSP_BUILD_VER_QUERY_CMD, NULL,
0, version_name, BUILD_ID_LEN);
if(ret)
return -ENOMSG;
snprintf(version_name, buf_size, "%s\n", version_name);
return 0;
}
void forever(void) { while (1); }
extern void ak_secondary_cpu_init(void);
extern void ak_secondary_cpu_reinit(void);
extern void send_event(void);
/*
* Set the cold/warm boot address for one of the CPU cores.
*/
int scm_set_boot_addr(bool enable_sec_core)
{
int ret;
struct {
unsigned int flags;
unsigned long addr;
} cmd;
if (enable_sec_core)
cmd.addr = (unsigned long)ak_secondary_cpu_init;
else
cmd.addr = (unsigned long)forever;
cmd.flags = SCM_FLAG_COLDBOOT_CPU1;
ret = scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR,
&cmd, sizeof(cmd), NULL, 0);
if (ret) {
printf("--- %s: scm_call failed ret = %d\n", __func__, ret);
}
return ret;
}
void clear_l2cache_err(void)
{
unsigned int val;
#ifndef CONFIG_SYS_DCACHE_OFF
val = get_l2_indirect_reg(L2ESR_IND_ADDR);
#endif
#ifdef CONFIG_IPQ_REPORT_L2ERR
report_l2err(val);
#endif
#ifndef CONFIG_SYS_DCACHE_OFF
set_l2_indirect_reg(L2ESR_IND_ADDR, val);
#endif
}
static int dcache_old_status;
void enable_caches(void)
{
icache_enable();
dcache_enable();
}
void disable_caches(void)
{
icache_disable();
dcache_disable();
}
/**
* Set the uuid in bootargs variable for mounting rootfilesystem
*/
int set_uuid_bootargs(char *boot_args, char *part_name, int buflen, bool gpt_flag)
{
return 0;
}
int is_secondary_core_off(unsigned int cpuid)
{
if (dcache_old_status)
dcache_enable();
return 1;
}
static int secondary_core_already_reset;
extern void wait_event(void (*)(void));
void bring_secondary_core_down(unsigned int state)
{
wait_event(ak_secondary_cpu_reinit);
}
static int krait_release_secondary(void)
{
dcache_disable();
writel(0xa4, CPU1_APCS_SAW2_VCTL);
barrier();
udelay(512);
writel(0x109, CPU1_APCS_CPU_PWR_CTL);
writel(0x101, CPU1_APCS_CPU_PWR_CTL);
barrier();
udelay(1);
writel(0x121, CPU1_APCS_CPU_PWR_CTL);
barrier();
udelay(2);
writel(0x120, CPU1_APCS_CPU_PWR_CTL);
barrier();
udelay(2);
writel(0x100, CPU1_APCS_CPU_PWR_CTL);
barrier();
udelay(100);
writel(0x180, CPU1_APCS_CPU_PWR_CTL);
barrier();
return 0;
}
int bring_sec_core_up(unsigned int cpuid, unsigned int entry, unsigned int arg)
{
dcache_old_status = dcache_status();
if (!secondary_core_already_reset) {
secondary_core_already_reset = 1;
if (scm_set_boot_addr(true) == 0) {
/* Pull Core-1 out of reset, iff scm call succeeds */
krait_release_secondary();
}
} else {
dcache_disable();
send_event();
}
return 0;
}
unsigned int get_dts_machid(unsigned int machid)
{
switch (machid)
{
case MACH_TYPE_IPQ806X_AP148_1XX:
return MACH_TYPE_IPQ806X_AP148;
default:
return machid;
}
}
void uart_wait_tx_empty(void)
{
ipq_serial_wait_tx_empty();
}