| /* |
| * 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(); |
| } |